diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-03 02:15:56 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-03 02:15:56 +0000 |
commit | 50b48aec6ddd451a6d1709c0942477b503457663 (patch) | |
tree | a9ece53ec06fd0a2819de7a2a6de997193566626 /libk3b/projects/datacd | |
download | k3b-50b48aec6ddd451a6d1709c0942477b503457663.tar.gz k3b-50b48aec6ddd451a6d1709c0942477b503457663.zip |
Added abandoned KDE3 version of K3B
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/k3b@1084400 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libk3b/projects/datacd')
28 files changed, 7527 insertions, 0 deletions
diff --git a/libk3b/projects/datacd/Makefile.am b/libk3b/projects/datacd/Makefile.am new file mode 100644 index 0000000..dea5cb2 --- /dev/null +++ b/libk3b/projects/datacd/Makefile.am @@ -0,0 +1,35 @@ +AM_CPPFLAGS= -I$(srcdir)/.. \ + -I$(srcdir)/../../core \ + -I$(srcdir)/../../plugin \ + -I$(srcdir)/../../../libk3bdevice \ + -I$(srcdir)/../../../src \ + -I$(srcdir)/../../tools \ + -I$(srcdir)/../../jobs \ + -I$(srcdir)/../.. \ + $(all_includes) + +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdata.la + +libdata_la_SOURCES = k3bdatajob.cpp \ + k3bdatadoc.cpp \ + k3bdataitem.cpp \ + k3bdiritem.cpp \ + k3bfileitem.cpp \ + k3bisoimager.cpp \ + k3bmsinfofetcher.cpp \ + k3bbootitem.cpp \ + k3bisooptions.cpp \ + k3bfilecompilationsizehandler.cpp \ + k3bsessionimportitem.cpp \ + k3bmkisofshandler.cpp \ + k3bdatapreparationjob.cpp + +include_HEADERS = k3bdatadoc.h \ + k3bdatajob.h \ + k3bdataitem.h \ + k3bdiritem.h \ + k3bfileitem.h \ + k3bbootitem.h \ + k3bisooptions.h diff --git a/libk3b/projects/datacd/k3bbootitem.cpp b/libk3b/projects/datacd/k3bbootitem.cpp new file mode 100644 index 0000000..e94830e --- /dev/null +++ b/libk3b/projects/datacd/k3bbootitem.cpp @@ -0,0 +1,58 @@ +/* + * + * $Id: k3bbootitem.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bbootitem.h" +#include "k3bdatadoc.h" +#include "k3bdiritem.h" + +#include <klocale.h> + +#include <qptrlist.h> + + +K3bBootItem::K3bBootItem( const QString& fileName, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName ) + : K3bFileItem( fileName, doc, dir, k3bName, FILE|BOOT_IMAGE ), + m_noBoot(false), + m_bootInfoTable(false), + m_loadSegment(0), + m_loadSize(0), + m_imageType(FLOPPY) +{ + setExtraInfo( i18n("El Torito Boot image") ); +} + + +K3bBootItem::K3bBootItem( const K3bBootItem& item ) + : K3bFileItem( item ), + m_noBoot( item.m_noBoot ), + m_bootInfoTable( item.m_bootInfoTable ), + m_loadSegment( item.m_loadSegment ), + m_loadSize( item.m_loadSize ), + m_imageType( item.m_imageType ), + m_tempPath( item.m_tempPath ) +{ +} + + +K3bBootItem::~K3bBootItem() +{ + take(); +} + + +K3bDataItem* K3bBootItem::copy() const +{ + return new K3bBootItem( *this ); +} diff --git a/libk3b/projects/datacd/k3bbootitem.h b/libk3b/projects/datacd/k3bbootitem.h new file mode 100644 index 0000000..9dd8704 --- /dev/null +++ b/libk3b/projects/datacd/k3bbootitem.h @@ -0,0 +1,66 @@ +/* + * + * $Id: k3bbootitem.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_BOOT_ITEM_H_ +#define _K3B_BOOT_ITEM_H_ + +#include "k3bfileitem.h" + +class K3bBootItem : public K3bFileItem +{ + public: + K3bBootItem( const QString& fileName, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName = 0 ); + K3bBootItem( const K3bBootItem& ); + ~K3bBootItem(); + + K3bDataItem* copy() const; + + bool isHideable() const { return false; } + + bool isBootItem() const { return true; } + + enum imageType { FLOPPY, HARDDISK, NONE }; + + void setNoBoot( bool b ) { m_noBoot = b; } + void setBootInfoTable( bool b ) { m_bootInfoTable = b; } + void setLoadSegment( int s ) { m_loadSegment = s; } + void setLoadSize( int s ) { m_loadSize = s; } + void setImageType( int t ) { m_imageType = t; } + + void setTempPath( const QString& p ) { m_tempPath = p; } + + bool noBoot() const { return m_noBoot; } + bool bootInfoTable() const { return m_bootInfoTable; } + int loadSegment() const { return m_loadSegment; } + int loadSize() const { return m_loadSize; } + int imageType() const { return m_imageType; } + + /** + * mkisofs changes boot images on disk. That is why the iso imager + * buffers them and saves the path to the buffered copy here. + */ + const QString& tempPath() const { return m_tempPath; } + + private: + bool m_noBoot; + bool m_bootInfoTable; + int m_loadSegment; + int m_loadSize; + int m_imageType; + + QString m_tempPath; +}; + +#endif diff --git a/libk3b/projects/datacd/k3bdatadoc.cpp b/libk3b/projects/datacd/k3bdatadoc.cpp new file mode 100644 index 0000000..d12c8d2 --- /dev/null +++ b/libk3b/projects/datacd/k3bdatadoc.cpp @@ -0,0 +1,1376 @@ +/* + * + * $Id: k3bdatadoc.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3bdatadoc.h" +#include "k3bfileitem.h" +#include "k3bdiritem.h" +#include "k3bsessionimportitem.h" +#include "k3bdatajob.h" +#include "k3bbootitem.h" +#include "k3bspecialdataitem.h" +#include "k3bfilecompilationsizehandler.h" +#include "k3bmkisofshandler.h" +#include <k3bcore.h> +#include <k3bglobals.h> +#include <k3bmsf.h> +#include <k3biso9660.h> +#include <k3bdevicehandler.h> +#include <k3bdevice.h> +#include <k3btoc.h> +#include <k3btrack.h> +#include <k3bmultichoicedialog.h> +#include <k3bvalidators.h> + +#include <qdir.h> +#include <qstring.h> +#include <qfileinfo.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qtimer.h> +#include <qdom.h> +#include <qptrlist.h> + +#include <kstandarddirs.h> +#include <kurl.h> +#include <kstatusbar.h> +#include <klocale.h> +#include <kinputdialog.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kprogress.h> +#include <kconfig.h> +#include <kapplication.h> + + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + + +/** + * There are two ways to fill a data project with files and folders: + * \li Use the addUrl and addUrls methods + * \li or create your own K3bDirItems and K3bFileItems. The doc will be properly updated + * by the constructors of the items. + */ +K3bDataDoc::K3bDataDoc( QObject* parent ) + : K3bDoc( parent ) +{ + m_root = 0; + + m_sizeHandler = new K3bFileCompilationSizeHandler(); +} + +K3bDataDoc::~K3bDataDoc() +{ + delete m_root; + delete m_sizeHandler; + // delete m_oldSessionSizeHandler; +} + + +bool K3bDataDoc::newDocument() +{ + clearImportedSession(); + + m_bootCataloge = 0; + m_oldSessionSize = 0; + m_bExistingItemsReplaceAll = m_bExistingItemsIgnoreAll = false; + + if( m_root ) { + while( m_root->children().getFirst() ) + removeItem( m_root->children().getFirst() ); + } + else + m_root = new K3bRootItem( this ); + + m_sizeHandler->clear(); + + m_multisessionMode = AUTO; + m_dataMode = K3b::DATA_MODE_AUTO; + + m_isoOptions = K3bIsoOptions(); + + return K3bDoc::newDocument(); +} + + +QString K3bDataDoc::name() const +{ + return m_isoOptions.volumeID(); +} + + +void K3bDataDoc::setIsoOptions( const K3bIsoOptions& o ) +{ + m_isoOptions = o; + emit changed(); +} + + +void K3bDataDoc::setVolumeID( const QString& v ) +{ + m_isoOptions.setVolumeID( v ); + emit changed(); +} + + +void K3bDataDoc::addUrls( const KURL::List& urls ) +{ + addUrls( urls, root() ); +} + + +void K3bDataDoc::addUrls( const KURL::List& l, K3bDirItem* dir ) +{ + if( !dir ) + dir = root(); + + KURL::List urls = K3b::convertToLocalUrls(l); + + for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) { + const KURL& url = *it; + QFileInfo f( url.path() ); + QString k3bname = f.absFilePath().section( "/", -1 ); + + // filenames cannot end in backslashes (mkisofs problem. See comments in k3bisoimager.cpp (escapeGraftPoint())) + while( k3bname[k3bname.length()-1] == '\\' ) + k3bname.truncate( k3bname.length()-1 ); + + // backup dummy name + if( k3bname.isEmpty() ) + k3bname = "1"; + + K3bDirItem* newDirItem = 0; + + // rename the new item if an item with that name already exists + int cnt = 0; + bool ok = false; + while( !ok ) { + ok = true; + QString name( k3bname ); + if( cnt > 0 ) + name += QString("_%1").arg(cnt); + if( K3bDataItem* oldItem = dir->find( name ) ) { + if( f.isDir() && oldItem->isDir() ) { + // ok, just reuse the dir + newDirItem = static_cast<K3bDirItem*>(oldItem); + } + // directories cannot replace files in an old session (I think) + // and also directories can for sure never be replaced (only be reused as above) + // so we always rename if the old item is a dir. + else if( !oldItem->isFromOldSession() || + f.isDir() || + oldItem->isDir() ) { + ++cnt; + ok = false; + } + } + } + if( cnt > 0 ) + k3bname += QString("_%1").arg(cnt); + + // QFileInfo::exists and QFileInfo::isReadable return false for broken symlinks :( + if( f.isDir() && !f.isSymLink() ) { + if( !newDirItem ) { + newDirItem = new K3bDirItem( k3bname, this, dir ); + newDirItem->setLocalPath( url.path() ); // HACK: see k3bdiritem.h + } + + // recursively add all the files in the directory + QStringList dlist = QDir( f.absFilePath() ).entryList( QDir::All|QDir::System|QDir::Hidden ); + dlist.remove("."); + dlist.remove(".."); + KURL::List newUrls; + for( QStringList::Iterator it = dlist.begin(); it != dlist.end(); ++it ) + newUrls.append( KURL::fromPathOrURL( f.absFilePath() + "/" + *it ) ); + addUrls( newUrls, newDirItem ); + } + else if( f.isSymLink() || f.isFile() ) + (void)new K3bFileItem( url.path(), this, dir, k3bname ); + } + + emit changed(); + + setModified( true ); +} + + +bool K3bDataDoc::nameAlreadyInDir( const QString& name, K3bDirItem* dir ) +{ + if( !dir ) + return false; + else + return ( dir->find( name ) != 0 ); +} + + +K3bDirItem* K3bDataDoc::addEmptyDir( const QString& name, K3bDirItem* parent ) +{ + K3bDirItem* item = new K3bDirItem( name, this, parent ); + + setModified( true ); + + return item; +} + + +KIO::filesize_t K3bDataDoc::size() const +{ + if( m_isoOptions.doNotCacheInodes() ) + return root()->blocks().mode1Bytes() + m_oldSessionSize; + else + return m_sizeHandler->blocks( m_isoOptions.followSymbolicLinks() || + !m_isoOptions.createRockRidge() ).mode1Bytes() + m_oldSessionSize; +} + + +KIO::filesize_t K3bDataDoc::burningSize() const +{ + return size() - m_oldSessionSize; //m_oldSessionSizeHandler->size(); +} + + +K3b::Msf K3bDataDoc::length() const +{ + // 1 block consists of 2048 bytes real data + // and 1 block equals to 1 audio frame + // so this is the way to calculate: + + return K3b::Msf( size() / 2048 ); +} + + +K3b::Msf K3bDataDoc::burningLength() const +{ + return K3b::Msf( burningSize() / 2048 ); +} + + +QString K3bDataDoc::typeString() const +{ + return QString::fromLatin1("data"); +} + + +bool K3bDataDoc::loadDocumentData( QDomElement* rootElem ) +{ + if( !root() ) + newDocument(); + + QDomNodeList nodes = rootElem->childNodes(); + + if( nodes.item(0).nodeName() != "general" ) { + kdDebug() << "(K3bDataDoc) could not find 'general' section." << endl; + return false; + } + if( !readGeneralDocumentData( nodes.item(0).toElement() ) ) + return false; + + + // parse options + // ----------------------------------------------------------------- + if( nodes.item(1).nodeName() != "options" ) { + kdDebug() << "(K3bDataDoc) could not find 'options' section." << endl; + return false; + } + if( !loadDocumentDataOptions( nodes.item(1).toElement() ) ) + return false; + // ----------------------------------------------------------------- + + + + // parse header + // ----------------------------------------------------------------- + if( nodes.item(2).nodeName() != "header" ) { + kdDebug() << "(K3bDataDoc) could not find 'header' section." << endl; + return false; + } + if( !loadDocumentDataHeader( nodes.item(2).toElement() ) ) + return false; + // ----------------------------------------------------------------- + + + + // parse files + // ----------------------------------------------------------------- + if( nodes.item(3).nodeName() != "files" ) { + kdDebug() << "(K3bDataDoc) could not find 'files' section." << endl; + return false; + } + + if( m_root == 0 ) + m_root = new K3bRootItem( this ); + + QDomNodeList filesList = nodes.item(3).childNodes(); + for( uint i = 0; i < filesList.count(); i++ ) { + + QDomElement e = filesList.item(i).toElement(); + if( !loadDataItem( e, root() ) ) + return false; + } + + // ----------------------------------------------------------------- + + // + // Old versions of K3b do not properly save the boot catalog location + // and name. So to ensure we have one around even if loading an old project + // file we create a default one here. + // + if( !m_bootImages.isEmpty() && !m_bootCataloge ) + createBootCatalogeItem( m_bootImages.first()->parent() ); + + + informAboutNotFoundFiles(); + + return true; +} + + +bool K3bDataDoc::loadDocumentDataOptions( QDomElement elem ) +{ + QDomNodeList headerList = elem.childNodes(); + for( uint i = 0; i < headerList.count(); i++ ) { + + QDomElement e = headerList.item(i).toElement(); + if( e.isNull() ) + return false; + + if( e.nodeName() == "rock_ridge") + m_isoOptions.setCreateRockRidge( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "joliet") + m_isoOptions.setCreateJoliet( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "udf") + m_isoOptions.setCreateUdf( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "joliet_allow_103_characters") + m_isoOptions.setJolietLong( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_allow_lowercase") + m_isoOptions.setISOallowLowercase( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_allow_period_at_begin") + m_isoOptions.setISOallowPeriodAtBegin( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_allow_31_char") + m_isoOptions.setISOallow31charFilenames( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_omit_version_numbers") + m_isoOptions.setISOomitVersionNumbers( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_omit_trailing_period") + m_isoOptions.setISOomitTrailingPeriod( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_max_filename_length") + m_isoOptions.setISOmaxFilenameLength( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_relaxed_filenames") + m_isoOptions.setISOrelaxedFilenames( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_no_iso_translate") + m_isoOptions.setISOnoIsoTranslate( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_allow_multidot") + m_isoOptions.setISOallowMultiDot( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_untranslated_filenames") + m_isoOptions.setISOuntranslatedFilenames( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "follow_symbolic_links") + m_isoOptions.setFollowSymbolicLinks( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "create_trans_tbl") + m_isoOptions.setCreateTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "hide_trans_tbl") + m_isoOptions.setHideTRANS_TBL( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "iso_level") + m_isoOptions.setISOLevel( e.text().toInt() ); + + else if( e.nodeName() == "discard_symlinks") + m_isoOptions.setDiscardSymlinks( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "discard_broken_symlinks") + m_isoOptions.setDiscardBrokenSymlinks( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "preserve_file_permissions") + m_isoOptions.setPreserveFilePermissions( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "force_input_charset") + m_isoOptions.setForceInputCharset( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "input_charset") + m_isoOptions.setInputCharset( e.text() ); + + else if( e.nodeName() == "do_not_cache_inodes" ) + m_isoOptions.setDoNotCacheInodes( e.attributeNode( "activated" ).value() == "yes" ); + + else if( e.nodeName() == "whitespace_treatment" ) { + if( e.text() == "strip" ) + m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::strip ); + else if( e.text() == "extended" ) + m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::extended ); + else if( e.text() == "extended" ) + m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::replace ); + else + m_isoOptions.setWhiteSpaceTreatment( K3bIsoOptions::noChange ); + } + + else if( e.nodeName() == "whitespace_replace_string") + m_isoOptions.setWhiteSpaceTreatmentReplaceString( e.text() ); + + else if( e.nodeName() == "data_track_mode" ) { + if( e.text() == "mode1" ) + m_dataMode = K3b::MODE1; + else if( e.text() == "mode2" ) + m_dataMode = K3b::MODE2; + else + m_dataMode = K3b::DATA_MODE_AUTO; + } + + else if( e.nodeName() == "multisession" ) { + QString mode = e.text(); + if( mode == "start" ) + setMultiSessionMode( START ); + else if( mode == "continue" ) + setMultiSessionMode( CONTINUE ); + else if( mode == "finish" ) + setMultiSessionMode( FINISH ); + else if( mode == "none" ) + setMultiSessionMode( NONE ); + else + setMultiSessionMode( AUTO ); + } + + else if( e.nodeName() == "verify_data" ) + setVerifyData( e.attributeNode( "activated" ).value() == "yes" ); + + else + kdDebug() << "(K3bDataDoc) unknown option entry: " << e.nodeName() << endl; + } + + return true; +} + + +bool K3bDataDoc::loadDocumentDataHeader( QDomElement headerElem ) +{ + QDomNodeList headerList = headerElem.childNodes(); + for( uint i = 0; i < headerList.count(); i++ ) { + + QDomElement e = headerList.item(i).toElement(); + if( e.isNull() ) + return false; + + if( e.nodeName() == "volume_id" ) + m_isoOptions.setVolumeID( e.text() ); + + else if( e.nodeName() == "application_id" ) + m_isoOptions.setApplicationID( e.text() ); + + else if( e.nodeName() == "publisher" ) + m_isoOptions.setPublisher( e.text() ); + + else if( e.nodeName() == "preparer" ) + m_isoOptions.setPreparer( e.text() ); + + else if( e.nodeName() == "volume_set_id" ) + m_isoOptions.setVolumeSetId( e.text() ); + + else if( e.nodeName() == "volume_set_size" ) + m_isoOptions.setVolumeSetSize( e.text().toInt() ); + + else if( e.nodeName() == "volume_set_number" ) + m_isoOptions.setVolumeSetNumber( e.text().toInt() ); + + else if( e.nodeName() == "system_id" ) + m_isoOptions.setSystemId( e.text() ); + + else + kdDebug() << "(K3bDataDoc) unknown header entry: " << e.nodeName() << endl; + } + + return true; +} + + +bool K3bDataDoc::loadDataItem( QDomElement& elem, K3bDirItem* parent ) +{ + K3bDataItem* newItem = 0; + + if( elem.nodeName() == "file" ) { + QDomElement urlElem = elem.firstChild().toElement(); + if( urlElem.isNull() ) { + kdDebug() << "(K3bDataDoc) file-element without url!" << endl; + return false; + } + + QFileInfo f( urlElem.text() ); + + // We canot use exists() here since this always disqualifies broken symlinks + if( !f.isFile() && !f.isSymLink() ) + m_notFoundFiles.append( urlElem.text() ); + + // broken symlinks are not readable according to QFileInfo which is wrong in our case + else if( f.isFile() && !f.isReadable() ) + m_noPermissionFiles.append( urlElem.text() ); + + else if( !elem.attribute( "bootimage" ).isEmpty() ) { + K3bBootItem* bootItem = new K3bBootItem( urlElem.text(), + this, + parent, + elem.attributeNode( "name" ).value() ); + if( elem.attribute( "bootimage" ) == "floppy" ) + bootItem->setImageType( K3bBootItem::FLOPPY ); + else if( elem.attribute( "bootimage" ) == "harddisk" ) + bootItem->setImageType( K3bBootItem::HARDDISK ); + else + bootItem->setImageType( K3bBootItem::NONE ); + bootItem->setNoBoot( elem.attribute( "no_boot" ) == "yes" ); + bootItem->setBootInfoTable( elem.attribute( "boot_info_table" ) == "yes" ); + bootItem->setLoadSegment( elem.attribute( "load_segment" ).toInt() ); + bootItem->setLoadSize( elem.attribute( "load_size" ).toInt() ); + + newItem = bootItem; + } + + else { + newItem = new K3bFileItem( urlElem.text(), + this, + parent, + elem.attributeNode( "name" ).value() ); + } + } + else if( elem.nodeName() == "special" ) { + if( elem.attributeNode( "type" ).value() == "boot cataloge" ) + createBootCatalogeItem( parent )->setK3bName( elem.attributeNode( "name" ).value() ); + } + else if( elem.nodeName() == "directory" ) { + // This is for the VideoDVD project which already contains the *_TS folders + K3bDirItem* newDirItem = 0; + if( K3bDataItem* item = parent->find( elem.attributeNode( "name" ).value() ) ) { + if( item->isDir() ) { + newDirItem = static_cast<K3bDirItem*>(item); + } + else { + kdError() << "(K3bDataDoc) INVALID DOCUMENT: item " << item->k3bPath() << " saved twice" << endl; + return false; + } + } + + if( !newDirItem ) + newDirItem = new K3bDirItem( elem.attributeNode( "name" ).value(), this, parent ); + QDomNodeList childNodes = elem.childNodes(); + for( uint i = 0; i < childNodes.count(); i++ ) { + + QDomElement e = childNodes.item(i).toElement(); + if( !loadDataItem( e, newDirItem ) ) + return false; + } + + newItem = newDirItem; + } + else { + kdDebug() << "(K3bDataDoc) wrong tag in files-section: " << elem.nodeName() << endl; + return false; + } + + // load the sort weight + if( newItem ) + newItem->setSortWeight( elem.attribute( "sort_weight", "0" ).toInt() ); + + return true; +} + + +bool K3bDataDoc::saveDocumentData( QDomElement* docElem ) +{ + QDomDocument doc = docElem->ownerDocument(); + + saveGeneralDocumentData( docElem ); + + // all options + // ---------------------------------------------------------------------- + QDomElement optionsElem = doc.createElement( "options" ); + saveDocumentDataOptions( optionsElem ); + docElem->appendChild( optionsElem ); + // ---------------------------------------------------------------------- + + // the header stuff + // ---------------------------------------------------------------------- + QDomElement headerElem = doc.createElement( "header" ); + saveDocumentDataHeader( headerElem ); + docElem->appendChild( headerElem ); + + + // now do the "real" work: save the entries + // ---------------------------------------------------------------------- + QDomElement topElem = doc.createElement( "files" ); + + QPtrListIterator<K3bDataItem> it( root()->children() ); + for( ; it.current(); ++it ) { + saveDataItem( it.current(), &doc, &topElem ); + } + + docElem->appendChild( topElem ); + // ---------------------------------------------------------------------- + + return true; +} + + +void K3bDataDoc::saveDocumentDataOptions( QDomElement& optionsElem ) +{ + QDomDocument doc = optionsElem.ownerDocument(); + + QDomElement topElem = doc.createElement( "rock_ridge" ); + topElem.setAttribute( "activated", isoOptions().createRockRidge() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "joliet" ); + topElem.setAttribute( "activated", isoOptions().createJoliet() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "udf" ); + topElem.setAttribute( "activated", isoOptions().createUdf() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "joliet_allow_103_characters" ); + topElem.setAttribute( "activated", isoOptions().jolietLong() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_allow_lowercase" ); + topElem.setAttribute( "activated", isoOptions().ISOallowLowercase() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_allow_period_at_begin" ); + topElem.setAttribute( "activated", isoOptions().ISOallowPeriodAtBegin() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_allow_31_char" ); + topElem.setAttribute( "activated", isoOptions().ISOallow31charFilenames() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_omit_version_numbers" ); + topElem.setAttribute( "activated", isoOptions().ISOomitVersionNumbers() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_omit_trailing_period" ); + topElem.setAttribute( "activated", isoOptions().ISOomitTrailingPeriod() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_max_filename_length" ); + topElem.setAttribute( "activated", isoOptions().ISOmaxFilenameLength() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_relaxed_filenames" ); + topElem.setAttribute( "activated", isoOptions().ISOrelaxedFilenames() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_no_iso_translate" ); + topElem.setAttribute( "activated", isoOptions().ISOnoIsoTranslate() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_allow_multidot" ); + topElem.setAttribute( "activated", isoOptions().ISOallowMultiDot() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_untranslated_filenames" ); + topElem.setAttribute( "activated", isoOptions().ISOuntranslatedFilenames() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "follow_symbolic_links" ); + topElem.setAttribute( "activated", isoOptions().followSymbolicLinks() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "create_trans_tbl" ); + topElem.setAttribute( "activated", isoOptions().createTRANS_TBL() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "hide_trans_tbl" ); + topElem.setAttribute( "activated", isoOptions().hideTRANS_TBL() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "iso_level" ); + topElem.appendChild( doc.createTextNode( QString::number(isoOptions().ISOLevel()) ) ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "discard_symlinks" ); + topElem.setAttribute( "activated", isoOptions().discardSymlinks() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "discard_broken_symlinks" ); + topElem.setAttribute( "activated", isoOptions().discardBrokenSymlinks() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "preserve_file_permissions" ); + topElem.setAttribute( "activated", isoOptions().preserveFilePermissions() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "force_input_charset" ); + topElem.setAttribute( "activated", isoOptions().forceInputCharset() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "do_not_cache_inodes" ); + topElem.setAttribute( "activated", isoOptions().doNotCacheInodes() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "input_charset" ); + topElem.appendChild( doc.createTextNode( isoOptions().inputCharset() ) ); + optionsElem.appendChild( topElem ); + + + topElem = doc.createElement( "whitespace_treatment" ); + switch( isoOptions().whiteSpaceTreatment() ) { + case K3bIsoOptions::strip: + topElem.appendChild( doc.createTextNode( "strip" ) ); + break; + case K3bIsoOptions::extended: + topElem.appendChild( doc.createTextNode( "extended" ) ); + break; + case K3bIsoOptions::replace: + topElem.appendChild( doc.createTextNode( "replace" ) ); + break; + default: + topElem.appendChild( doc.createTextNode( "noChange" ) ); + break; + } + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "whitespace_replace_string" ); + topElem.appendChild( doc.createTextNode( isoOptions().whiteSpaceTreatmentReplaceString() ) ); + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "data_track_mode" ); + if( m_dataMode == K3b::MODE1 ) + topElem.appendChild( doc.createTextNode( "mode1" ) ); + else if( m_dataMode == K3b::MODE2 ) + topElem.appendChild( doc.createTextNode( "mode2" ) ); + else + topElem.appendChild( doc.createTextNode( "auto" ) ); + optionsElem.appendChild( topElem ); + + + // save multisession + topElem = doc.createElement( "multisession" ); + switch( m_multisessionMode ) { + case START: + topElem.appendChild( doc.createTextNode( "start" ) ); + break; + case CONTINUE: + topElem.appendChild( doc.createTextNode( "continue" ) ); + break; + case FINISH: + topElem.appendChild( doc.createTextNode( "finish" ) ); + break; + case NONE: + topElem.appendChild( doc.createTextNode( "none" ) ); + break; + default: + topElem.appendChild( doc.createTextNode( "auto" ) ); + break; + } + optionsElem.appendChild( topElem ); + + topElem = doc.createElement( "verify_data" ); + topElem.setAttribute( "activated", verifyData() ? "yes" : "no" ); + optionsElem.appendChild( topElem ); + // ---------------------------------------------------------------------- +} + + +void K3bDataDoc::saveDocumentDataHeader( QDomElement& headerElem ) +{ + QDomDocument doc = headerElem.ownerDocument(); + + QDomElement topElem = doc.createElement( "volume_id" ); + topElem.appendChild( doc.createTextNode( isoOptions().volumeID() ) ); + headerElem.appendChild( topElem ); + + topElem = doc.createElement( "volume_set_id" ); + topElem.appendChild( doc.createTextNode( isoOptions().volumeSetId() ) ); + headerElem.appendChild( topElem ); + + topElem = doc.createElement( "volume_set_size" ); + topElem.appendChild( doc.createTextNode( QString::number(isoOptions().volumeSetSize()) ) ); + headerElem.appendChild( topElem ); + + topElem = doc.createElement( "volume_set_number" ); + topElem.appendChild( doc.createTextNode( QString::number(isoOptions().volumeSetNumber()) ) ); + headerElem.appendChild( topElem ); + + topElem = doc.createElement( "system_id" ); + topElem.appendChild( doc.createTextNode( isoOptions().systemId() ) ); + headerElem.appendChild( topElem ); + + topElem = doc.createElement( "application_id" ); + topElem.appendChild( doc.createTextNode( isoOptions().applicationID() ) ); + headerElem.appendChild( topElem ); + + topElem = doc.createElement( "publisher" ); + topElem.appendChild( doc.createTextNode( isoOptions().publisher() ) ); + headerElem.appendChild( topElem ); + + topElem = doc.createElement( "preparer" ); + topElem.appendChild( doc.createTextNode( isoOptions().preparer() ) ); + headerElem.appendChild( topElem ); + // ---------------------------------------------------------------------- +} + + +void K3bDataDoc::saveDataItem( K3bDataItem* item, QDomDocument* doc, QDomElement* parent ) +{ + if( K3bFileItem* fileItem = dynamic_cast<K3bFileItem*>( item ) ) { + if( m_oldSession.contains( fileItem ) ) { + kdDebug() << "(K3bDataDoc) ignoring fileitem " << fileItem->k3bName() << " from old session while saving..." << endl; + } + else { + QDomElement topElem = doc->createElement( "file" ); + topElem.setAttribute( "name", fileItem->k3bName() ); + QDomElement subElem = doc->createElement( "url" ); + subElem.appendChild( doc->createTextNode( fileItem->localPath() ) ); + topElem.appendChild( subElem ); + + if( item->sortWeight() != 0 ) + topElem.setAttribute( "sort_weight", QString::number(item->sortWeight()) ); + + parent->appendChild( topElem ); + + // add boot options as attributes to preserve compatibility to older K3b versions + if( K3bBootItem* bootItem = dynamic_cast<K3bBootItem*>( fileItem ) ) { + if( bootItem->imageType() == K3bBootItem::FLOPPY ) + topElem.setAttribute( "bootimage", "floppy" ); + else if( bootItem->imageType() == K3bBootItem::HARDDISK ) + topElem.setAttribute( "bootimage", "harddisk" ); + else + topElem.setAttribute( "bootimage", "none" ); + + topElem.setAttribute( "no_boot", bootItem->noBoot() ? "yes" : "no" ); + topElem.setAttribute( "boot_info_table", bootItem->bootInfoTable() ? "yes" : "no" ); + topElem.setAttribute( "load_segment", QString::number( bootItem->loadSegment() ) ); + topElem.setAttribute( "load_size", QString::number( bootItem->loadSize() ) ); + } + } + } + else if( item == m_bootCataloge ) { + QDomElement topElem = doc->createElement( "special" ); + topElem.setAttribute( "name", m_bootCataloge->k3bName() ); + topElem.setAttribute( "type", "boot cataloge" ); + + parent->appendChild( topElem ); + } + else if( K3bDirItem* dirItem = dynamic_cast<K3bDirItem*>( item ) ) { + QDomElement topElem = doc->createElement( "directory" ); + topElem.setAttribute( "name", dirItem->k3bName() ); + + if( item->sortWeight() != 0 ) + topElem.setAttribute( "sort_weight", QString::number(item->sortWeight()) ); + + QPtrListIterator<K3bDataItem> it( dirItem->children() ); + for( ; it.current(); ++it ) { + saveDataItem( it.current(), doc, &topElem ); + } + + parent->appendChild( topElem ); + } +} + + +void K3bDataDoc::removeItem( K3bDataItem* item ) +{ + if( !item ) + return; + + if( item->isRemoveable() ) { + delete item; + } + else + kdDebug() << "(K3bDataDoc) tried to remove non-removable entry!" << endl; +} + + +void K3bDataDoc::itemRemovedFromDir( K3bDirItem*, K3bDataItem* removedItem ) +{ + // update the project size + if( !removedItem->isFromOldSession() ) + m_sizeHandler->removeFile( removedItem ); + + // update the boot item list + if( removedItem->isBootItem() ) { + m_bootImages.removeRef( static_cast<K3bBootItem*>( removedItem ) ); + if( m_bootImages.isEmpty() ) { + delete m_bootCataloge; + m_bootCataloge = 0; + } + } + + emit itemRemoved( removedItem ); + emit changed(); +} + + +void K3bDataDoc::itemAddedToDir( K3bDirItem*, K3bDataItem* item ) +{ + // update the project size + if( !item->isFromOldSession() ) + m_sizeHandler->addFile( item ); + + // update the boot item list + if( item->isBootItem() ) + m_bootImages.append( static_cast<K3bBootItem*>( item ) ); + + emit itemAdded( item ); + emit changed(); +} + + +void K3bDataDoc::moveItem( K3bDataItem* item, K3bDirItem* newParent ) +{ + if( !item || !newParent ) { + kdDebug() << "(K3bDataDoc) item or parentitem was NULL while moving." << endl; + return; + } + + if( !item->isMoveable() ) { + kdDebug() << "(K3bDataDoc) item is not movable! " << endl; + return; + } + + item->reparent( newParent ); +} + + +void K3bDataDoc::moveItems( QPtrList<K3bDataItem> itemList, K3bDirItem* newParent ) +{ + if( !newParent ) { + kdDebug() << "(K3bDataDoc) tried to move items to nowhere...!" << endl; + return; + } + + QPtrListIterator<K3bDataItem> it( itemList ); + for( ; it.current(); ++it ) { + // check if newParent is subdir of item + if( K3bDirItem* dirItem = dynamic_cast<K3bDirItem*>( it.current() ) ) { + if( dirItem->isSubItem( newParent ) ) { + continue; + } + } + + if( it.current()->isMoveable() ) + it.current()->reparent( newParent ); + } +} + + +K3bBurnJob* K3bDataDoc::newBurnJob( K3bJobHandler* hdl, QObject* parent ) +{ + return new K3bDataJob( this, hdl, parent ); +} + + +QString K3bDataDoc::treatWhitespace( const QString& path ) +{ + + // TODO: + // It could happen that two files with different names + // will have the same name after the treatment + // Perhaps we should add a number at the end or something + // similar (s.a.) + + + if( isoOptions().whiteSpaceTreatment() != K3bIsoOptions::noChange ) { + QString result = path; + + if( isoOptions().whiteSpaceTreatment() == K3bIsoOptions::replace ) { + result.replace( ' ', isoOptions().whiteSpaceTreatmentReplaceString() ); + } + else if( isoOptions().whiteSpaceTreatment() == K3bIsoOptions::strip ) { + result.remove( ' ' ); + } + else if( isoOptions().whiteSpaceTreatment() == K3bIsoOptions::extended ) { + result.truncate(0); + for( uint i = 0; i < path.length(); i++ ) { + if( path[i] == ' ' ) { + if( path[i+1] != ' ' ) + result.append( path[++i].upper() ); + } + else + result.append( path[i] ); + } + } + + kdDebug() << "(K3bDataDoc) converted " << path << " to " << result << endl; + return result; + } + else + return path; +} + + +void K3bDataDoc::prepareFilenames() +{ + m_needToCutFilenames = false; + m_needToCutFilenameItems.clear(); + + // + // if joliet is used cut the names and rename if necessary + // 64 characters for standard joliet and 103 characters for long joliet names + // + // Rockridge supports the full 255 UNIX chars and in case Rockridge is disabled we leave + // it to mkisofs for now since handling all the options to alter the ISO9660 standard it just + // too much. + // + + K3bDataItem* item = root(); + unsigned int maxlen = ( isoOptions().jolietLong() ? 103 : 64 ); + while( (item = item->nextSibling()) ) { + item->setWrittenName( treatWhitespace( item->k3bName() ) ); + + if( isoOptions().createJoliet() && item->writtenName().length() > maxlen ) { + m_needToCutFilenames = true; + item->setWrittenName( K3b::cutFilename( item->writtenName(), maxlen ) ); + m_needToCutFilenameItems.append( item ); + } + + // TODO: check the Joliet charset + } + + // + // 3. check if a directory contains items with the same name + // + prepareFilenamesInDir( root() ); +} + + +void K3bDataDoc::prepareFilenamesInDir( K3bDirItem* dir ) +{ + if( !dir ) + return; + + QPtrList<K3bDataItem> sortedChildren; + QPtrListIterator<K3bDataItem> it( dir->children() ); + + for( it.toLast(); it.current(); --it ) { + K3bDataItem* item = it.current(); + + if( item->isDir() ) + prepareFilenamesInDir( dynamic_cast<K3bDirItem*>( item ) ); + + // insertion sort + unsigned int i = 0; + while( i < sortedChildren.count() && item->writtenName() > sortedChildren.at(i)->writtenName() ) + ++i; + + sortedChildren.insert( i, item ); + } + + + if( isoOptions().createJoliet() || isoOptions().createRockRidge() ) { + QPtrList<K3bDataItem> sameNameList; + while( !sortedChildren.isEmpty() ) { + + sameNameList.clear(); + + do { + sameNameList.append( sortedChildren.first() ); + sortedChildren.removeFirst(); + } while( !sortedChildren.isEmpty() && + sortedChildren.first()->writtenName() == sameNameList.first()->writtenName() ); + + if( sameNameList.count() > 1 ) { + // now we need to rename the items + unsigned int maxlen = 255; + if( isoOptions().createJoliet() ) { + if( isoOptions().jolietLong() ) + maxlen = 103; + else + maxlen = 64; + } + + int cnt = 1; + for( QPtrListIterator<K3bDataItem> it( sameNameList ); + it.current(); ++it ) { + it.current()->setWrittenName( K3b::appendNumberToFilename( it.current()->writtenName(), cnt++, maxlen ) ); + } + } + } + } +} + + +void K3bDataDoc::informAboutNotFoundFiles() +{ + if( !m_notFoundFiles.isEmpty() ) { + KMessageBox::informationList( qApp->activeWindow(), i18n("Could not find the following files:"), + m_notFoundFiles, i18n("Not Found") ); + m_notFoundFiles.clear(); + } + + if( !m_noPermissionFiles.isEmpty() ) { + KMessageBox::informationList( qApp->activeWindow(), i18n("No permission to read the following files:"), + m_noPermissionFiles, i18n("No Read Permission") ); + + m_noPermissionFiles.clear(); + } +} + + +void K3bDataDoc::setMultiSessionMode( K3bDataDoc::MultiSessionMode mode ) +{ + if( m_multisessionMode == NONE || m_multisessionMode == START ) + clearImportedSession(); + + m_multisessionMode = mode; +} + + +bool K3bDataDoc::importSession( K3bDevice::Device* device ) +{ + K3bDevice::DiskInfo diskInfo = device->diskInfo(); + // DVD+RW media is reported as non-appendable + if( !diskInfo.appendable() && + !(diskInfo.mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR)) ) + return false; + + K3bDevice::Toc toc = device->readToc(); + if( toc.isEmpty() || + toc.last().type() != K3bDevice::Track::DATA ) + return false; + + long startSec = toc.last().firstSector().lba(); + K3bIso9660 iso( device, startSec ); + + if( iso.open() ) { + // remove previously imported sessions + clearImportedSession(); + + // set multisession option + if( m_multisessionMode != FINISH && m_multisessionMode != AUTO ) + m_multisessionMode = CONTINUE; + + // since in iso9660 it is possible that two files share it's data + // simply summing the file sizes could result in wrong values + // that's why we use the size from the toc. This is more accurate + // anyway since there might be files overwritten or removed + m_oldSessionSize = toc.last().lastSector().mode1Bytes(); + + kdDebug() << "(K3bDataDoc) imported session size: " << KIO::convertSize(m_oldSessionSize) << endl; + + // the track size for DVD+RW media and DVD-RW Overwrite media has nothing to do with the filesystem + // size. in that case we need to use the filesystem's size (which is ok since it's one track anyway, + // no real multisession) + if( diskInfo.mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) ) { + m_oldSessionSize = iso.primaryDescriptor().volumeSpaceSize + * iso.primaryDescriptor().logicalBlockSize; + } + + // import some former settings + m_isoOptions.setCreateRockRidge( iso.firstRRDirEntry() != 0 ); + m_isoOptions.setCreateJoliet( iso.firstJolietDirEntry() != 0 ); + m_isoOptions.setVolumeID( iso.primaryDescriptor().volumeId ); + // TODO: also import some other pd fields + + const K3bIso9660Directory* rootDir = iso.firstRRDirEntry(); + // Jörg Schilling says that it is impossible to import the joliet tree for multisession +// if( !rootDir ) +// rootDir = iso.firstJolietDirEntry(); + if( !rootDir ) + rootDir = iso.firstIsoDirEntry(); + + if( rootDir ) { + createSessionImportItems( rootDir, root() ); + emit changed(); + return true; + } + else { + kdDebug() << "(K3bDataDoc::importSession) Could not find primary volume desc." << endl; + return false; + } + } + else { + kdDebug() << "(K3bDataDoc) unable to read toc." << endl; + return false; + } +} + + +void K3bDataDoc::createSessionImportItems( const K3bIso9660Directory* importDir, K3bDirItem* parent ) +{ + Q_ASSERT(importDir); + QStringList entries = importDir->entries(); + entries.remove( "." ); + entries.remove( ".." ); + for( QStringList::const_iterator it = entries.begin(); + it != entries.end(); ++it ) { + const K3bIso9660Entry* entry = importDir->entry( *it ); + K3bDataItem* oldItem = parent->find( entry->name() ); + if( entry->isDirectory() ) { + K3bDirItem* dir = 0; + if( oldItem && oldItem->isDir() ) { + dir = (K3bDirItem*)oldItem; + } + else { + // we overwrite without warning! + if( oldItem ) + removeItem( oldItem ); + dir = new K3bDirItem( entry->name(), this, parent ); + } + + dir->setRemoveable(false); + dir->setRenameable(false); + dir->setMoveable(false); + dir->setHideable(false); + dir->setWriteToCd(false); + dir->setExtraInfo( i18n("From previous session") ); + m_oldSession.append( dir ); + + createSessionImportItems( static_cast<const K3bIso9660Directory*>(entry), dir ); + } + else { + const K3bIso9660File* file = static_cast<const K3bIso9660File*>(entry); + + // we overwrite without warning! + if( oldItem ) + removeItem( oldItem ); + + K3bSessionImportItem* item = new K3bSessionImportItem( file, this, parent ); + item->setExtraInfo( i18n("From previous session") ); + m_oldSession.append( item ); + } + } +} + + +void K3bDataDoc::clearImportedSession() +{ + // m_oldSessionSizeHandler->clear(); + m_oldSessionSize = 0; + m_oldSession.setAutoDelete(false); + K3bDataItem* item = m_oldSession.first(); + while( !m_oldSession.isEmpty() ) { + if( item == 0 ) + item = m_oldSession.first(); + + if( item->isDir() ) { + K3bDirItem* dir = (K3bDirItem*)item; + if( dir->numDirs() + dir->numFiles() == 0 ) { + // this imported dir is not needed anymore + // since it is empty + m_oldSession.remove(); + delete dir; + } + else { + for( QPtrListIterator<K3bDataItem> it( dir->children() ); it.current(); ++it ) { + if( !m_oldSession.contains(it.current()) ) { + m_oldSession.remove(); + // now the dir becomes a totally normal dir + dir->setRemoveable(true); + dir->setRenameable(true); + dir->setMoveable(true); + dir->setHideable(true); + dir->setWriteToCd(true); + dir->setExtraInfo( "" ); + break; + } + } + } + } + else { + m_oldSession.remove(); + delete item; + } + + item = m_oldSession.next(); + } + + m_multisessionMode = AUTO; + + emit changed(); +} + + +K3bDirItem* K3bDataDoc::bootImageDir() +{ + K3bDataItem* b = m_root->find( "boot" ); + if( !b ) { + b = new K3bDirItem( "boot", this, m_root ); + setModified( true ); + } + + // if we cannot create the dir because there is a file named boot just use the root dir + if( !b->isDir() ) + return m_root; + else + return static_cast<K3bDirItem*>(b); +} + + +K3bBootItem* K3bDataDoc::createBootItem( const QString& filename, K3bDirItem* dir ) +{ + if( !dir ) + dir = bootImageDir(); + + K3bBootItem* boot = new K3bBootItem( filename, this, dir ); + + if( !m_bootCataloge ) + createBootCatalogeItem(dir); + + return boot; +} + + +K3bDataItem* K3bDataDoc::createBootCatalogeItem( K3bDirItem* dir ) +{ + if( !m_bootCataloge ) { + QString newName = "boot.catalog"; + int i = 0; + while( dir->alreadyInDirectory( "boot.catalog" ) ) { + ++i; + newName = QString( "boot%1.catalog" ).arg(i); + } + + K3bSpecialDataItem* b = new K3bSpecialDataItem( this, 0, dir, newName ); + m_bootCataloge = b; + m_bootCataloge->setRemoveable(false); + m_bootCataloge->setHideable(false); + m_bootCataloge->setWriteToCd(false); + m_bootCataloge->setExtraInfo( i18n("El Torito boot catalog file") ); + b->setMimeType( i18n("Boot catalog") ); + } + else + m_bootCataloge->reparent( dir ); + + return m_bootCataloge; +} + + +QValueList<K3bDataItem*> K3bDataDoc::findItemByLocalPath( const QString& path ) const +{ + Q_UNUSED( path ); + return QValueList<K3bDataItem*>(); +} + + +bool K3bDataDoc::sessionImported() const +{ + return !m_oldSession.isEmpty(); +} + +#include "k3bdatadoc.moc" diff --git a/libk3b/projects/datacd/k3bdatadoc.h b/libk3b/projects/datacd/k3bdatadoc.h new file mode 100644 index 0000000..e09177a --- /dev/null +++ b/libk3b/projects/datacd/k3bdatadoc.h @@ -0,0 +1,297 @@ +/* + * + * $Id: k3bdatadoc.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef K3BDATADOC_H +#define K3BDATADOC_H + +#include <k3bdoc.h> +#include <k3bdataitem.h> + +#include "k3bisooptions.h" + +#include <qptrlist.h> +#include <qfileinfo.h> +#include <qstringlist.h> + +#include <kurl.h> +#include <kio/global.h> +#include "k3b_export.h" + +class K3bDataItem; +class K3bRootItem; +class K3bDirItem; +class K3bFileItem; +class K3bJob; +class K3bBootItem; +class K3bFileCompilationSizeHandler; + +class KProgressDialog; +//class K3bView; +class KConfig; +class QString; +class QStringList; +class QWidget; +class QDomDocument; +class QDomElement; +class K3bIso9660Directory; + +namespace K3bDevice { + class Device; + class DeviceHandler; +} + + +/** + *@author Sebastian Trueg + */ + +class LIBK3B_EXPORT K3bDataDoc : public K3bDoc +{ + Q_OBJECT + + public: + K3bDataDoc( QObject* parent = 0 ); + virtual ~K3bDataDoc(); + + virtual int type() const { return DATA; } + virtual QString typeString() const; + + virtual QString name() const; + + enum MultiSessionMode { + /** + * Let the K3bDataJob decide if to close the CD or not. + * The decision is based on the state of the inserted media + * (appendable/closed), the size of the project (will it fill + * up the CD?), and the free space on the inserted media. + */ + AUTO, + NONE, + START, + CONTINUE, + FINISH + }; + + K3bRootItem* root() const { return m_root; } + + virtual bool newDocument(); + virtual KIO::filesize_t size() const; + + /** + * This is used for multisession where size() also returnes the imported session's size + */ + virtual KIO::filesize_t burningSize() const; + virtual K3b::Msf length() const; + virtual K3b::Msf burningLength() const; + + /** + * Simply deletes the item if it is removable (meaning isRemovable() returns true. + * Be aware that you can remove items simply by deleting them even if isRemovable() + * returns false. + */ + void removeItem( K3bDataItem* item ); + + /** + * Simply calls reparent. + */ + void moveItem( K3bDataItem* item, K3bDirItem* newParent ); + void moveItems( QPtrList<K3bDataItem> itemList, K3bDirItem* newParent ); + + K3bDirItem* addEmptyDir( const QString& name, K3bDirItem* parent ); + + QString treatWhitespace( const QString& ); + + virtual K3bBurnJob* newBurnJob( K3bJobHandler* hdl, QObject* parent = 0 ); + + MultiSessionMode multiSessionMode() const { return m_multisessionMode; } + void setMultiSessionMode( MultiSessionMode mode ); + + int dataMode() const { return m_dataMode; } + void setDataMode( int m ) { m_dataMode = m; } + + void setVerifyData( bool b ) { m_verifyData = b; } + bool verifyData() const { return m_verifyData; } + + static bool nameAlreadyInDir( const QString&, K3bDirItem* ); + + /** + * Most of the options that map to the mkisofs parameters are grouped + * together in the K3bIsoOptions class to allow easy saving to and loading + * from a KConfig object. + */ + const K3bIsoOptions& isoOptions() const { return m_isoOptions; } + void setIsoOptions( const K3bIsoOptions& ); + + const QPtrList<K3bBootItem>& bootImages() { return m_bootImages; } + K3bDataItem* bootCataloge() { return m_bootCataloge; } + + K3bDirItem* bootImageDir(); + + /** + * Create a boot item and also create a boot cataloge file in case none + * exists in the project. + * + * Calling this method has the same effect like creating a new K3bBootItem + * instance manually and then calling createBootCatalogeItem. + * + * \return The new boot item on success or 0 in case a file with the same + * name already exists. + */ + K3bBootItem* createBootItem( const QString& filename, K3bDirItem* bootDir = 0 ); + + /** + * Create a new boot catalog item. + * For now this is not called automatically for internal reasons. + * + * Call this if you create boot items manually instead of using createBootItem. + * + * The boot catalog is automatically deleted once the last boot item is removed + * from the doc. + * + * \return The new boot catalog item or the old one if it already exists. + */ + K3bDataItem* createBootCatalogeItem( K3bDirItem* bootDir ); + + /** + * This will prepare the filenames as written to the image. + * These filenames are saved in K3bDataItem::writtenName + */ + void prepareFilenames(); + + /** + * Returns true if filenames need to be cut due to the limitations of Joliet. + * + * This is only valid after a call to @p prepareFilenames() + */ + bool needToCutFilenames() const { return m_needToCutFilenames; } + + const QValueList<K3bDataItem*>& needToCutFilenameItems() const { return m_needToCutFilenameItems; } + + /** + * Imports a session into the project. This will create K3bSessionImportItems + * and properly set the imported session size. + * Some settings will be adjusted to the imported session (joliet, rr). + * + * Be aware that this method is blocking. + * + * \return true if the old session was successfully imported, false if no + * session could be found. + * + * \see clearImportedSession() + */ + bool importSession( K3bDevice::Device* ); + + bool sessionImported() const; + + /** + * Searches for an item by it's local path. + * + * NOT IMPLEMENTED YET! + * + * \return The items that correspond to the specified local path. + */ + QValueList<K3bDataItem*> findItemByLocalPath( const QString& path ) const; + + public slots: + virtual void addUrls( const KURL::List& urls ); + + /** + * Add urls syncroneously + * This method adds files recursively including symlinks, hidden, and system files. + * If a file already exists the new file's name will be appended a number. + */ + virtual void addUrls( const KURL::List& urls, K3bDirItem* dir ); + + void clearImportedSession(); + + /** + * Just a convience method to prevent using setIsoOptions for this + * often used value. + */ + void setVolumeID( const QString& ); + + signals: + void itemRemoved( K3bDataItem* ); + void itemAdded( K3bDataItem* ); + + protected: + /** reimplemented from K3bDoc */ + virtual bool loadDocumentData( QDomElement* root ); + /** reimplemented from K3bDoc */ + virtual bool saveDocumentData( QDomElement* ); + + void saveDocumentDataOptions( QDomElement& optionsElem ); + void saveDocumentDataHeader( QDomElement& headerElem ); + bool loadDocumentDataOptions( QDomElement optionsElem ); + bool loadDocumentDataHeader( QDomElement optionsElem ); + + K3bFileCompilationSizeHandler* m_sizeHandler; + + // K3bFileCompilationSizeHandler* m_oldSessionSizeHandler; + KIO::filesize_t m_oldSessionSize; + + private: + void prepareFilenamesInDir( K3bDirItem* dir ); + void createSessionImportItems( const K3bIso9660Directory*, K3bDirItem* parent ); + + /** + * used by K3bDirItem to inform about removed items. + */ + void itemRemovedFromDir( K3bDirItem* parent, K3bDataItem* removedItem ); + void itemAddedToDir( K3bDirItem* parent, K3bDataItem* addedItem ); + + /** + * load recursivly + */ + bool loadDataItem( QDomElement& e, K3bDirItem* parent ); + /** + * save recursivly + */ + void saveDataItem( K3bDataItem* item, QDomDocument* doc, QDomElement* parent ); + + void informAboutNotFoundFiles(); + + QStringList m_notFoundFiles; + QStringList m_noPermissionFiles; + + K3bRootItem* m_root; + + int m_dataMode; + + bool m_verifyData; + + KIO::filesize_t m_size; + + K3bIsoOptions m_isoOptions; + + MultiSessionMode m_multisessionMode; + QPtrList<K3bDataItem> m_oldSession; + + // boot cd stuff + K3bDataItem* m_bootCataloge; + QPtrList<K3bBootItem> m_bootImages; + + bool m_bExistingItemsReplaceAll; + bool m_bExistingItemsIgnoreAll; + + bool m_needToCutFilenames; + QValueList<K3bDataItem*> m_needToCutFilenameItems; + + friend class K3bMixedDoc; + friend class K3bDirItem; +}; + +#endif diff --git a/libk3b/projects/datacd/k3bdataitem.cpp b/libk3b/projects/datacd/k3bdataitem.cpp new file mode 100644 index 0000000..6f2a861 --- /dev/null +++ b/libk3b/projects/datacd/k3bdataitem.cpp @@ -0,0 +1,264 @@ +/* + * + * $Id: k3bdataitem.cpp 659634 2007-04-30 14:51:32Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3bdataitem.h" +#include "k3bdiritem.h" +#include "k3bdatadoc.h" +#include <kdebug.h> + +#include <math.h> + + +class K3bDataItem::Private +{ +public: + int flags; +}; + + +K3bDataItem::K3bDataItem( K3bDataDoc* doc, K3bDataItem* parent, int flags ) + : m_bHideOnRockRidge(false), + m_bHideOnJoliet(false), + m_bRemoveable(true), + m_bRenameable(true), + m_bMovable(true), + m_bHideable(true), + m_bWriteToCd(true), + m_sortWeight(0) +{ + d = new Private; + d->flags = flags; + + m_doc = doc; + m_bHideOnRockRidge = m_bHideOnJoliet = false; + + if( parent ) + m_parentDir = parent->getDirItem(); + else + m_parentDir = 0; +} + + +K3bDataItem::K3bDataItem( const K3bDataItem& item ) + : m_k3bName( item.m_k3bName ), + m_doc( 0 ), + m_parentDir( 0 ), + m_bHideOnRockRidge( item.m_bHideOnRockRidge ), + m_bHideOnJoliet( item.m_bHideOnJoliet ), + m_bRemoveable( item.m_bRemoveable ), + m_bRenameable( item.m_bRenameable ), + m_bMovable( item.m_bMovable ), + m_bHideable( item.m_bHideable ), + m_bWriteToCd( item.m_bWriteToCd ), + m_extraInfo( item.m_extraInfo ), + m_sortWeight( item.m_sortWeight ) +{ + d = new Private; + d->flags = item.d->flags; +} + + +K3bDataItem::~K3bDataItem() +{ + delete d; +} + + +void K3bDataItem::setFlags( int flags ) +{ + d->flags = flags; +} + + +bool K3bDataItem::isBootItem() const +{ + return d->flags & BOOT_IMAGE; +} + + +KIO::filesize_t K3bDataItem::size() const +{ + return itemSize( m_doc + ? m_doc->isoOptions().followSymbolicLinks() || + !m_doc->isoOptions().createRockRidge() + : false ); +} + + +K3b::Msf K3bDataItem::blocks() const +{ + return itemBlocks( m_doc + ? m_doc->isoOptions().followSymbolicLinks() || + !m_doc->isoOptions().createRockRidge() + : false ); +} + + +K3b::Msf K3bDataItem::itemBlocks( bool followSymbolicLinks ) const +{ + return (long)::ceil( (double)itemSize( followSymbolicLinks ) / 2048.0 ); +} + + +void K3bDataItem::setK3bName( const QString& name ) { + if ( name != m_k3bName ) { + // test for not-allowed characters + if( name.contains('/') ) { + kdDebug() << "(K3bDataItem) name contained invalid characters!" << endl; + return; + } + + if( parent() ) { + K3bDataItem* item = parent()->find( name ); + if( item && item != this ) { + kdDebug() << "(K3bDataItem) item with that name already exists." << endl; + return; + } + } + + m_k3bName = name; + m_doc->setModified(); +} +} + + +const QString& K3bDataItem::k3bName() const +{ + return m_k3bName; +} + + +K3bDataItem* K3bDataItem::take() +{ + if( parent() ) + parent()->takeDataItem( this ); + + return this; +} + + +QString K3bDataItem::k3bPath() const +{ + if( !getParent() ) + return QString::null; // the root item is the only one not having a parent + else if( isDir() ) + return getParent()->k3bPath() + k3bName() + "/"; + else + return getParent()->k3bPath() + k3bName(); +} + + +QString K3bDataItem::writtenPath() const +{ + if( !getParent() ) + return QString::null; // the root item is the only one not having a parent + else if( isDir() ) + return getParent()->writtenPath() + writtenName() + "/"; + else + return getParent()->writtenPath() + writtenName(); +} + + +QString K3bDataItem::iso9660Path() const +{ + if( !getParent() ) + return QString::null; // the root item is the only one not having a parent + else if( isDir() ) + return getParent()->iso9660Path() + iso9660Name() + "/"; + else + return getParent()->iso9660Path() + iso9660Name(); +} + + +K3bDataItem* K3bDataItem::nextSibling() const +{ + K3bDataItem* item = const_cast<K3bDataItem*>(this); // urg, but we know that we don't mess with it, so... + K3bDirItem* parentItem = getParent(); + + while( parentItem ) { + if( K3bDataItem* i = parentItem->nextChild( item ) ) + return i; + + item = parentItem; + parentItem = item->getParent(); + } + + return 0; +} + + +void K3bDataItem::reparent( K3bDirItem* newParent ) +{ + // addDataItem will do all the stuff including taking this + newParent->addDataItem( this ); +} + + +bool K3bDataItem::hideOnRockRidge() const +{ + if( !isHideable() ) + return false; + if( getParent() ) + return m_bHideOnRockRidge || getParent()->hideOnRockRidge(); + else + return m_bHideOnRockRidge; +} + + +bool K3bDataItem::hideOnJoliet() const +{ + if( !isHideable() ) + return false; + if( getParent() ) + return m_bHideOnJoliet || getParent()->hideOnJoliet(); + else + return m_bHideOnJoliet; +} + + +void K3bDataItem::setHideOnRockRidge( bool b ) +{ + // there is no use in changing the value if + // it is already set by the parent + if( ( !getParent() || !getParent()->hideOnRockRidge() ) && + b != m_bHideOnRockRidge ) { + m_bHideOnRockRidge = b; + if ( m_doc ) + m_doc->setModified(); +} +} + + +void K3bDataItem::setHideOnJoliet( bool b ) +{ + // there is no use in changing the value if + // it is already set by the parent + if( ( !getParent() || !getParent()->hideOnJoliet() ) && + b != m_bHideOnJoliet ) { + m_bHideOnJoliet = b; + if ( m_doc ) + m_doc->setModified(); +} +} + + +int K3bDataItem::depth() const +{ + if( getParent() ) + return getParent()->depth() + 1; + else + return 0; +} diff --git a/libk3b/projects/datacd/k3bdataitem.h b/libk3b/projects/datacd/k3bdataitem.h new file mode 100644 index 0000000..36cdf05 --- /dev/null +++ b/libk3b/projects/datacd/k3bdataitem.h @@ -0,0 +1,225 @@ +/* + * + * $Id: k3bdataitem.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef K3BDATAITEM_H +#define K3BDATAITEM_H + + +class K3bDirItem; +class K3bDataDoc; + +#include <qstring.h> + +#include <kio/global.h> + +#include <k3bmsf.h> +#include "k3b_export.h" + + +/** + *@author Sebastian Trueg + */ +class LIBK3B_EXPORT K3bDataItem +{ + public: + K3bDataItem( K3bDataDoc* doc, K3bDataItem* parent = 0, int flags = 0 ); + + /** + * Default copy constructor. + * + * The result is an exact copy except that no parent dir it set and, thus, also no doc. + */ + K3bDataItem( const K3bDataItem& ); + + virtual ~K3bDataItem(); + + /** + * Return an exact copy of this data item. + * + * The result is an exact copy except that no parent dir it set and, thus, also no doc. + * + * Implementations should use the default constructor. + */ + virtual K3bDataItem* copy() const = 0; + + K3bDirItem* parent() { return m_parentDir; } + K3bDirItem* getParent() const { return m_parentDir; } + + /** + * Remove this item from it's parent and return a pointer to it. + */ + K3bDataItem* take(); + + K3bDataDoc* doc() const { return m_doc; } + virtual const QString& k3bName() const; + virtual void setK3bName( const QString& ); + + /** + * returns the path as defined by the k3b-hierachy, NOT starting with a slash + * (since this is used for graft-points!) + * directories have a trailing "/" + */ + virtual QString k3bPath() const; + + /** + * Returns the name of the item as used on the CD or DVD image. + * + * This is only valid after a call to @p K3bDataDoc::prepareFilenames() + */ + const QString& writtenName() const { return m_writtenName; } + + /** + * \return the pure name used in the Iso9660 tree. + * + * This is only valid after a call to @p K3bDataDoc::prepareFilenames() + */ + const QString& iso9660Name() const { return m_rawIsoName; } + + /** + * Returns the path of the item as written to the CD or DVD image. + * + * This is suited to be used for mkisofs graftpoints. + * + * This is only valid after a call to @p K3bDataDoc::prepareFilenames() + */ + virtual QString writtenPath() const; + + virtual QString iso9660Path() const; + + /** + * Used to set the written name by @p K3bDataDoc::prepareFilenames() + */ + void setWrittenName( const QString& s ) { m_writtenName = s; } + + /** + * Used to set the pure Iso9660 name by @p K3bDataDoc::prepareFilenames() + */ + void setIso9660Name( const QString& s ) { m_rawIsoName = s; } + + virtual K3bDataItem* nextSibling() const; + + /** returns the path to the file on the local filesystem */ + virtual QString localPath() const { return QString::null; } + + /** + * The size of the item + */ + KIO::filesize_t size() const; + + /** + * \return The number of blocks (2048 bytes) occupied by this item. + * This value equals to ceil(size()/2048) + */ + K3b::Msf blocks() const; + + /** + * \returne the dir of the item (or the item itself if it is a dir) + */ + virtual K3bDirItem* getDirItem() const { return getParent(); } + + virtual void reparent( K3bDirItem* ); + + // FIXME: use all these flags and make the isXXX methods + // non-virtual. Then move the parent()->addDataItem call + // to the K3bDataItem constructor + enum ItemFlags { + DIR = 0x1, + FILE = 0x2, + SPECIALFILE = 0x4, + SYMLINK = 0x8, + OLD_SESSION = 0x10, + BOOT_IMAGE = 0x11 + }; + + int flags() const; + + virtual bool isDir() const { return false; } + virtual bool isFile() const { return false; } + virtual bool isSpecialFile() const { return false; } + virtual bool isSymLink() const { return false; } + virtual bool isFromOldSession() const { return false; } + bool isBootItem() const; + + bool hideOnRockRidge() const; + bool hideOnJoliet() const; + + virtual void setHideOnRockRidge( bool b ); + virtual void setHideOnJoliet( bool b ); + + virtual long sortWeight() const { return m_sortWeight; } + virtual void setSortWeight( long w ) { m_sortWeight = w; } + + virtual int depth() const; + + virtual bool isValid() const { return true; } + + // these are all needed for special fileitems like + // imported sessions or the movix filesystem + virtual bool isRemoveable() const { return m_bRemoveable; } + virtual bool isMoveable() const { return m_bMovable; } + virtual bool isRenameable() const { return m_bRenameable; } + virtual bool isHideable() const { return m_bHideable; } + virtual bool writeToCd() const { return m_bWriteToCd; } + virtual const QString& extraInfo() const { return m_extraInfo; } + + void setRenameable( bool b ) { m_bRenameable = b; } + void setMoveable( bool b ) { m_bMovable = b; } + void setRemoveable( bool b ) { m_bRemoveable = b; } + void setHideable( bool b ) { m_bHideable = b; } + void setWriteToCd( bool b ) { m_bWriteToCd = b; } + void setExtraInfo( const QString& i ) { m_extraInfo = i; } + + protected: + virtual KIO::filesize_t itemSize( bool followSymlinks ) const = 0; + + /** + * \param followSymlinks If true symlinks will be followed and their + * size equals the size of the file they are + * pointing to. + * + * \return The number of blocks (2048 bytes) occupied by this item. + */ + virtual K3b::Msf itemBlocks( bool followSymlinks ) const; + + QString m_k3bName; + + void setFlags( int flags ); + + private: + class Private; + Private* d; + + QString m_writtenName; + QString m_rawIsoName; + + K3bDataDoc* m_doc; + K3bDirItem* m_parentDir; + + bool m_bHideOnRockRidge; + bool m_bHideOnJoliet; + bool m_bRemoveable; + bool m_bRenameable; + bool m_bMovable; + bool m_bHideable; + bool m_bWriteToCd; + QString m_extraInfo; + + long m_sortWeight; + + friend class K3bDirItem; +}; + +#endif diff --git a/libk3b/projects/datacd/k3bdatajob.cpp b/libk3b/projects/datacd/k3bdatajob.cpp new file mode 100644 index 0000000..7009a43 --- /dev/null +++ b/libk3b/projects/datacd/k3bdatajob.cpp @@ -0,0 +1,972 @@ +/* + * + * $Id: k3bdatajob.cpp 690187 2007-07-20 09:18:03Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3bdatajob.h" +#include "k3bdatadoc.h" +#include "k3bisoimager.h" +#include "k3bmsinfofetcher.h" + +#include <k3bcore.h> +#include <k3bglobals.h> +#include <k3bversion.h> +#include <k3bdevice.h> +#include <k3bdevicehandler.h> +#include <k3btoc.h> +#include <k3btrack.h> +#include <k3bdevicehandler.h> +#include <k3bexternalbinmanager.h> +#include <k3bcdrecordwriter.h> +#include <k3bcdrdaowriter.h> +#include <k3bglobalsettings.h> +#include <k3bactivepipe.h> +#include <k3bfilesplitter.h> +#include <k3bverificationjob.h> + +#include <kprocess.h> +#include <kapplication.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <ktempfile.h> +#include <kio/global.h> +#include <kio/job.h> + +#include <qstring.h> +#include <qstringlist.h> +#include <qdatetime.h> +#include <qfile.h> +#include <qdatastream.h> +#include <kdebug.h> + + + +class K3bDataJob::Private +{ +public: + Private() + : usedWritingApp(K3b::CDRECORD), + verificationJob(0) { + } + + K3bDataDoc* doc; + + bool initializingImager; + bool imageFinished; + bool canceled; + + KTempFile* tocFile; + + int usedDataMode; + int usedWritingApp; + int usedWritingMode; + K3bDataDoc::MultiSessionMode usedMultiSessionMode; + + int copies; + int copiesDone; + + K3bVerificationJob* verificationJob; + + K3bFileSplitter imageFile; + K3bActivePipe pipe; +}; + + +K3bDataJob::K3bDataJob( K3bDataDoc* doc, K3bJobHandler* hdl, QObject* parent ) + : K3bBurnJob( hdl, parent ) +{ + d = new Private; + + d->doc = doc; + m_writerJob = 0; + d->tocFile = 0; + + m_isoImager = 0; + + m_msInfoFetcher = new K3bMsInfoFetcher( this, this ); + connect( m_msInfoFetcher, SIGNAL(finished(bool)), this, SLOT(slotMsInfoFetched(bool)) ); + connect( m_msInfoFetcher, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) ); + connect( m_msInfoFetcher, SIGNAL(debuggingOutput(const QString&, const QString&)), + this, SIGNAL(debuggingOutput(const QString&, const QString&)) ); + + d->imageFinished = true; +} + +K3bDataJob::~K3bDataJob() +{ + delete d->tocFile; + delete d; +} + + +K3bDoc* K3bDataJob::doc() const +{ + return d->doc; +} + + +K3bDevice::Device* K3bDataJob::writer() const +{ + if( doc()->onlyCreateImages() ) + return 0; // no writer needed -> no blocking on K3bBurnJob + else + return doc()->burner(); +} + + +void K3bDataJob::start() +{ + jobStarted(); + + d->canceled = false; + d->imageFinished = false; + d->copies = d->doc->copies(); + d->copiesDone = 0; + d->usedMultiSessionMode = d->doc->multiSessionMode(); + + prepareImager(); + + if( d->doc->dummy() ) { + d->doc->setVerifyData( false ); + d->copies = 1; + } + + emit newTask( i18n("Preparing data") ); + + // there is no harm in setting these even if we write on-the-fly + d->imageFile.setName( d->doc->tempDir() ); + d->pipe.readFromIODevice( &d->imageFile ); + + if( d->usedMultiSessionMode == K3bDataDoc::AUTO && !d->doc->onlyCreateImages() ) + determineMultiSessionMode(); + else + prepareWriting(); +} + + +void K3bDataJob::prepareWriting() +{ + if( !d->doc->onlyCreateImages() && + ( d->usedMultiSessionMode == K3bDataDoc::CONTINUE || + d->usedMultiSessionMode == K3bDataDoc::FINISH ) ) { + // no sense continuing the same session twice + // FIXME: why not? + d->copies = 1; + + m_msInfoFetcher->setDevice( d->doc->burner() ); + + if( !waitForMedium() ) { + cancel(); + return; + } + + if( K3b::isMounted( d->doc->burner() ) ) { + emit infoMessage( i18n("Unmounting disk"), INFO ); + K3b::unmount( d->doc->burner() ); + } + + m_msInfoFetcher->start(); + } + else { + m_isoImager->setMultiSessionInfo( QString::null ); + prepareData(); + + d->initializingImager = true; + m_isoImager->init(); + } +} + + +void K3bDataJob::slotMsInfoFetched(bool success) +{ + if( success ) { + // we call this here since in ms mode we might want to check + // the last track's datamode + prepareData(); + + if( d->usedWritingApp == K3b::CDRDAO ) // cdrdao seems to write a 150 blocks pregap that is not used by cdrecord + m_isoImager->setMultiSessionInfo( QString("%1,%2").arg(m_msInfoFetcher->lastSessionStart()).arg(m_msInfoFetcher->nextSessionStart()+150), d->doc->burner() ); + else + m_isoImager->setMultiSessionInfo( m_msInfoFetcher->msInfo(), d->doc->burner() ); + + d->initializingImager = true; + m_isoImager->init(); + } + else { + // the MsInfoFetcher already emitted failure info + cancelAll(); + jobFinished( false ); + } +} + + +void K3bDataJob::writeImage() +{ + d->initializingImager = false; + + emit burning(false); + + // get image file path + if( d->doc->tempDir().isEmpty() ) + d->doc->setTempDir( K3b::findUniqueFilePrefix( d->doc->isoOptions().volumeID() ) + ".iso" ); + + // TODO: check if the image file is part of the project and if so warn the user + // and append some number to make the path unique. + + emit newTask( i18n("Creating image file") ); + emit newSubTask( i18n("Track 1 of 1") ); + emit infoMessage( i18n("Creating image file in %1").arg(d->doc->tempDir()), INFO ); + + m_isoImager->writeToImageFile( d->doc->tempDir() ); + m_isoImager->start(); +} + + +bool K3bDataJob::startOnTheFlyWriting() +{ + if( prepareWriterJob() ) { + if( startWriterJob() ) { + // try a direct connection between the processes + if( m_writerJob->fd() != -1 ) + m_isoImager->writeToFd( m_writerJob->fd() ); + d->initializingImager = false; + m_isoImager->start(); + return true; + } + } + return false; +} + + +void K3bDataJob::cancel() +{ + emit infoMessage( i18n("Writing canceled."), K3bJob::ERROR ); + emit canceled(); + + if( m_writerJob && m_writerJob->active() ) { + // + // lets wait for the writer job to finish + // and let it finish the job for good. + // + cancelAll(); + } + else { + // + // Just cancel all and return + // This is bad design as we should wait for all subjobs to finish + // + cancelAll(); + jobFinished( false ); + } +} + + +void K3bDataJob::slotIsoImagerPercent( int p ) +{ + if( d->doc->onlyCreateImages() ) { + emit subPercent( p ); + emit percent( p ); + } + else if( !d->doc->onTheFly() ) { + double totalTasks = d->copies; + double tasksDone = d->copiesDone; // =0 when creating an image + if( d->doc->verifyData() ) { + totalTasks*=2; + tasksDone*=2; + } + if( !d->doc->onTheFly() ) { + totalTasks+=1.0; + } + + emit subPercent( p ); + emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) ); + } +} + + +void K3bDataJob::slotIsoImagerFinished( bool success ) +{ + if( d->initializingImager ) { + if( success ) { + if( d->doc->onTheFly() && !d->doc->onlyCreateImages() ) { + if( !startOnTheFlyWriting() ) { + cancelAll(); + jobFinished( false ); + } + } + else { + writeImage(); + } + } + else { + if( m_isoImager->hasBeenCanceled() ) + emit canceled(); + jobFinished( false ); + } + } + else { + // tell the writer that there won't be more data + if( d->doc->onTheFly() && m_writerJob ) + m_writerJob->closeFd(); + + if( !d->doc->onTheFly() || + d->doc->onlyCreateImages() ) { + + if( success ) { + emit infoMessage( i18n("Image successfully created in %1").arg(d->doc->tempDir()), K3bJob::SUCCESS ); + d->imageFinished = true; + + if( d->doc->onlyCreateImages() ) { + jobFinished( true ); + } + else { + if( prepareWriterJob() ) { + startWriterJob(); + d->pipe.writeToFd( m_writerJob->fd(), true ); + d->pipe.open(true); + } + } + } + else { + if( m_isoImager->hasBeenCanceled() ) + emit canceled(); + else + emit infoMessage( i18n("Error while creating ISO image"), ERROR ); + + cancelAll(); + jobFinished( false ); + } + } + else if( !success ) { // on-the-fly + // + // In case the imager failed let's make sure the writer does not emit an unusable + // error message. + // + if( m_writerJob && m_writerJob->active() ) + m_writerJob->setSourceUnreadable( true ); + + // there is one special case which we need to handle here: the iso imager might be canceled + // FIXME: the iso imager should not be able to cancel itself + if( m_isoImager->hasBeenCanceled() && !this->hasBeenCanceled() ) + cancel(); + } + } +} + + +bool K3bDataJob::startWriterJob() +{ + if( d->doc->dummy() ) + emit newTask( i18n("Simulating") ); + else if( d->copies > 1 ) + emit newTask( i18n("Writing Copy %1").arg(d->copiesDone+1) ); + else + emit newTask( i18n("Writing") ); + + // if we append a new session we asked for an appendable cd already + if( d->usedMultiSessionMode == K3bDataDoc::NONE || + d->usedMultiSessionMode == K3bDataDoc::START ) { + + if( !waitForMedium() ) { + return false; + } + } + + emit burning(true); + m_writerJob->start(); + return true; +} + + +void K3bDataJob::slotWriterJobPercent( int p ) +{ + double totalTasks = d->copies; + double tasksDone = d->copiesDone; + if( d->doc->verifyData() ) { + totalTasks*=2; + tasksDone*=2; + } + if( !d->doc->onTheFly() ) { + totalTasks+=1.0; + tasksDone+=1.0; + } + + emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) ); +} + + +void K3bDataJob::slotWriterNextTrack( int t, int tt ) +{ + emit newSubTask( i18n("Writing Track %1 of %2").arg(t).arg(tt) ); +} + + +void K3bDataJob::slotWriterJobFinished( bool success ) +{ + d->pipe.close(); + + // + // This is a little workaround for the bad cancellation handling in this job + // see cancel() + // + if( d->canceled ) { + if( active() ) + jobFinished( false ); + } + + if( success ) { + // allright + // the writerJob should have emited the "simulation/writing successful" signal + + if( d->doc->verifyData() ) { + if( !d->verificationJob ) { + d->verificationJob = new K3bVerificationJob( this, this ); + connect( d->verificationJob, SIGNAL(infoMessage(const QString&, int)), + this, SIGNAL(infoMessage(const QString&, int)) ); + connect( d->verificationJob, SIGNAL(newTask(const QString&)), + this, SIGNAL(newSubTask(const QString&)) ); + connect( d->verificationJob, SIGNAL(newSubTask(const QString&)), + this, SIGNAL(newSubTask(const QString&)) ); + connect( d->verificationJob, SIGNAL(percent(int)), + this, SLOT(slotVerificationProgress(int)) ); + connect( d->verificationJob, SIGNAL(percent(int)), + this, SIGNAL(subPercent(int)) ); + connect( d->verificationJob, SIGNAL(finished(bool)), + this, SLOT(slotVerificationFinished(bool)) ); + connect( d->verificationJob, SIGNAL(debuggingOutput(const QString&, const QString&)), + this, SIGNAL(debuggingOutput(const QString&, const QString&)) ); + + } + d->verificationJob->clear(); + d->verificationJob->setDevice( d->doc->burner() ); + d->verificationJob->setGrownSessionSize( m_isoImager->size() ); + d->verificationJob->addTrack( 0, m_isoImager->checksum(), m_isoImager->size() ); + + emit burning(false); + + emit newTask( i18n("Verifying written data") ); + + d->verificationJob->start(); + } + else { + d->copiesDone++; + + if( d->copiesDone < d->copies ) { + K3bDevice::eject( d->doc->burner() ); + + bool failed = false; + if( d->doc->onTheFly() ) + failed = !startOnTheFlyWriting(); + else + failed = !startWriterJob(); + + if( failed ) { + cancel(); + } + else if( !d->doc->onTheFly() ) { + d->pipe.writeToFd( m_writerJob->fd(), true ); + d->pipe.open(true); + } + } + else { + cleanup(); + jobFinished(true); + } + } + } + else { + cancelAll(); + jobFinished( false ); + } +} + + +void K3bDataJob::slotVerificationProgress( int p ) +{ + double totalTasks = d->copies*2; + double tasksDone = d->copiesDone*2 + 1; // the writing of the current copy has already been finished + + if( !d->doc->onTheFly() ) { + totalTasks+=1.0; + tasksDone+=1.0; + } + + emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) ); +} + + +void K3bDataJob::slotVerificationFinished( bool success ) +{ + d->copiesDone++; + + // reconnect our imager which we deconnected for the verification + connectImager(); + + if( k3bcore->globalSettings()->ejectMedia() || d->copiesDone < d->copies ) + K3bDevice::eject( d->doc->burner() ); + + if( !d->canceled && d->copiesDone < d->copies ) { + bool failed = false; + if( d->doc->onTheFly() ) + failed = !startOnTheFlyWriting(); + else + failed = !startWriterJob(); + + if( failed ) + cancel(); + else if( !d->doc->onTheFly() ) { + d->pipe.writeToFd( m_writerJob->fd(), true ); + d->pipe.open(true); + } + } + else { + cleanup(); + jobFinished( success ); + } +} + + +void K3bDataJob::setWriterJob( K3bAbstractWriter* writer ) +{ + // FIXME: progressedsize for multiple copies + m_writerJob = writer; + connect( m_writerJob, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) ); + connect( m_writerJob, SIGNAL(percent(int)), this, SLOT(slotWriterJobPercent(int)) ); + connect( m_writerJob, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) ); + connect( m_writerJob, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) ); + connect( m_writerJob, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) ); + connect( m_writerJob, SIGNAL(nextTrack(int, int)), this, SLOT(slotWriterNextTrack(int, int)) ); + connect( m_writerJob, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) ); + connect( m_writerJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) ); + connect( m_writerJob, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) ); + connect( m_writerJob, SIGNAL(finished(bool)), this, SLOT(slotWriterJobFinished(bool)) ); + connect( m_writerJob, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) ); + connect( m_writerJob, SIGNAL(debuggingOutput(const QString&, const QString&)), + this, SIGNAL(debuggingOutput(const QString&, const QString&)) ); +} + + +void K3bDataJob::setImager( K3bIsoImager* imager ) +{ + if( m_isoImager != imager ) { + delete m_isoImager; + + m_isoImager = imager; + + connectImager(); + } +} + + +void K3bDataJob::connectImager() +{ + m_isoImager->disconnect( this ); + connect( m_isoImager, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) ); + connect( m_isoImager, SIGNAL(percent(int)), this, SLOT(slotIsoImagerPercent(int)) ); + connect( m_isoImager, SIGNAL(finished(bool)), this, SLOT(slotIsoImagerFinished(bool)) ); + connect( m_isoImager, SIGNAL(debuggingOutput(const QString&, const QString&)), + this, SIGNAL(debuggingOutput(const QString&, const QString&)) ); +} + + +void K3bDataJob::prepareImager() +{ + if( !m_isoImager ) + setImager( new K3bIsoImager( d->doc, this, this ) ); +} + + +bool K3bDataJob::prepareWriterJob() +{ + if( m_writerJob ) + return true; + + // It seems as if cdrecord is not able to append sessions in dao mode whereas cdrdao is + if( d->usedWritingApp == K3b::CDRECORD ) { + K3bCdrecordWriter* writer = new K3bCdrecordWriter( d->doc->burner(), this, this ); + + // cdrecord manpage says that "not all" writers are able to write + // multisession disks in dao mode. That means there are writers that can. + + // Does it really make sence to write DAta ms cds in DAO mode since writing the + // first session of a cd-extra in DAO mode is no problem with my writer while + // writing the second data session is only possible in TAO mode. + if( d->usedWritingMode == K3b::DAO && + d->usedMultiSessionMode != K3bDataDoc::NONE ) + emit infoMessage( i18n("Most writers do not support writing " + "multisession CDs in DAO mode."), INFO ); + + writer->setWritingMode( d->usedWritingMode ); + writer->setSimulate( d->doc->dummy() ); + writer->setBurnSpeed( d->doc->speed() ); + + // multisession + if( d->usedMultiSessionMode == K3bDataDoc::START || + d->usedMultiSessionMode == K3bDataDoc::CONTINUE ) { + writer->addArgument("-multi"); + } + + if( d->doc->onTheFly() && + ( d->usedMultiSessionMode == K3bDataDoc::CONTINUE || + d->usedMultiSessionMode == K3bDataDoc::FINISH ) ) + writer->addArgument("-waiti"); + + if( d->usedDataMode == K3b::MODE1 ) + writer->addArgument( "-data" ); + else { + if( k3bcore->externalBinManager()->binObject("cdrecord") && + k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "xamix" ) ) + writer->addArgument( "-xa" ); + else + writer->addArgument( "-xa1" ); + } + + writer->addArgument( QString("-tsize=%1s").arg(m_isoImager->size()) )->addArgument("-"); + + setWriterJob( writer ); + } + else { + // create cdrdao job + K3bCdrdaoWriter* writer = new K3bCdrdaoWriter( d->doc->burner(), this, this ); + writer->setCommand( K3bCdrdaoWriter::WRITE ); + writer->setSimulate( d->doc->dummy() ); + writer->setBurnSpeed( d->doc->speed() ); + // multisession + writer->setMulti( d->usedMultiSessionMode == K3bDataDoc::START || + d->usedMultiSessionMode == K3bDataDoc::CONTINUE ); + + // now write the tocfile + if( d->tocFile ) delete d->tocFile; + d->tocFile = new KTempFile( QString::null, "toc" ); + d->tocFile->setAutoDelete(true); + + if( QTextStream* s = d->tocFile->textStream() ) { + if( d->usedDataMode == K3b::MODE1 ) { + *s << "CD_ROM" << "\n"; + *s << "\n"; + *s << "TRACK MODE1" << "\n"; + } + else { + *s << "CD_ROM_XA" << "\n"; + *s << "\n"; + *s << "TRACK MODE2_FORM1" << "\n"; + } + + *s << "DATAFILE \"-\" " << m_isoImager->size()*2048 << "\n"; + + d->tocFile->close(); + } + else { + kdDebug() << "(K3bDataJob) could not write tocfile." << endl; + emit infoMessage( i18n("IO Error"), ERROR ); + cancelAll(); + return false; + } + + writer->setTocFile( d->tocFile->name() ); + + setWriterJob( writer ); + } + + return true; +} + + +void K3bDataJob::prepareData() +{ + // we don't need this when only creating image and it is possible + // that the burn device is null + if( d->doc->onlyCreateImages() ) + return; + + // first of all we determine the data mode + if( d->doc->dataMode() == K3b::DATA_MODE_AUTO ) { + if( !d->doc->onlyCreateImages() && + ( d->usedMultiSessionMode == K3bDataDoc::CONTINUE || + d->usedMultiSessionMode == K3bDataDoc::FINISH ) ) { + + // try to get the last track's datamode + // we already asked for an appendable cdr when fetching + // the ms info + kdDebug() << "(K3bDataJob) determining last track's datamode..." << endl; + + // FIXME: use a devicethread + K3bDevice::Toc toc = d->doc->burner()->readToc(); + if( toc.isEmpty() ) { + kdDebug() << "(K3bDataJob) could not retrieve toc." << endl; + emit infoMessage( i18n("Unable to determine the last track's datamode. Using default."), ERROR ); + d->usedDataMode = K3b::MODE2; + } + else { + if( toc[toc.count()-1].mode() == K3bDevice::Track::MODE1 ) + d->usedDataMode = K3b::MODE1; + else + d->usedDataMode = K3b::MODE2; + + kdDebug() << "(K3bDataJob) using datamode: " + << (d->usedDataMode == K3b::MODE1 ? "mode1" : "mode2") + << endl; + } + } + else if( d->usedMultiSessionMode == K3bDataDoc::NONE ) + d->usedDataMode = K3b::MODE1; + else + d->usedDataMode = K3b::MODE2; + } + else + d->usedDataMode = d->doc->dataMode(); + + + // determine the writing mode + if( d->doc->writingMode() == K3b::WRITING_MODE_AUTO ) { + // TODO: put this into the cdreocrdwriter and decide based on the size of the + // track + if( writer()->dao() && d->usedDataMode == K3b::MODE1 && + d->usedMultiSessionMode == K3bDataDoc::NONE ) + d->usedWritingMode = K3b::DAO; + else + d->usedWritingMode = K3b::TAO; + } + else + d->usedWritingMode = d->doc->writingMode(); + + + // cdrecord seems to have problems writing xa 1 disks in dao mode? At least on my system! + if( writingApp() == K3b::DEFAULT ) { + if( d->usedWritingMode == K3b::DAO ) { + if( d->usedMultiSessionMode != K3bDataDoc::NONE ) + d->usedWritingApp = K3b::CDRDAO; + else if( d->usedDataMode == K3b::MODE2 ) + d->usedWritingApp = K3b::CDRDAO; + else + d->usedWritingApp = K3b::CDRECORD; + } + else + d->usedWritingApp = K3b::CDRECORD; + } + else + d->usedWritingApp = writingApp(); +} + + +void K3bDataJob::determineMultiSessionMode() +{ + // + // THIS IS ONLY CALLED IF d->doc->multiSessionMode() == K3bDataDoc::AUTO! + // + + if( d->doc->writingMode() == K3b::WRITING_MODE_AUTO || + d->doc->writingMode() == K3b::TAO ) { + emit newSubTask( i18n("Searching for old session") ); + + // + // Wait for the medium. + // In case an old session was imported we always want to continue or finish a multisession CD/DVD. + // Otherwise we wait for everything we could handle and decide what to do in + // determineMultiSessionMode( K3bDevice::DeviceHandler* ) below. + // + + int wantedMediaState = K3bDevice::STATE_INCOMPLETE|K3bDevice::STATE_EMPTY; + if( d->doc->sessionImported() ) + wantedMediaState = K3bDevice::STATE_INCOMPLETE; + + int m = waitForMedia( d->doc->burner(), + wantedMediaState, + K3bDevice::MEDIA_WRITABLE_CD ); + + if( m < 0 ) + cancel(); + else { + // now we need to determine the media's size + connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::NG_DISKINFO, d->doc->burner() ), + SIGNAL(finished(K3bDevice::DeviceHandler*)), + this, + SLOT(slotDetermineMultiSessionMode(K3bDevice::DeviceHandler*)) ); + } + } + else { + // we need TAO for multisession + d->usedMultiSessionMode = K3bDataDoc::NONE; + + // carry on with the writing + prepareWriting(); + } +} + + +void K3bDataJob::slotDetermineMultiSessionMode( K3bDevice::DeviceHandler* dh ) +{ + // + // This is a little workaround for the bad cancellation handling in this job + // see cancel() + // + if( d->canceled ) { + if( active() ) { + cleanup(); + jobFinished( false ); + } + } + else { + d->usedMultiSessionMode = getMultiSessionMode( dh->diskInfo() ); + + // carry on with the writing + prepareWriting(); + } +} + + +K3bDataDoc::MultiSessionMode K3bDataJob::getMultiSessionMode( const K3bDevice::DiskInfo& info ) +{ + if( info.appendable() ) { + // + // 3 cases: + // 1. the project does not fit -> no multisession (resulting in asking for another media above) + // 2. the project does fit and fills up the CD -> finish multisession + // 3. the project does fit and does not fill up the CD -> continue multisession + // + // In case a session has been imported we do not consider NONE at all. + // + if( d->doc->size() > info.remainingSize().mode1Bytes() && !d->doc->sessionImported() ) + d->usedMultiSessionMode = K3bDataDoc::NONE; + else if( d->doc->size() >= info.remainingSize().mode1Bytes()*9/10 ) + d->usedMultiSessionMode = K3bDataDoc::FINISH; + else + d->usedMultiSessionMode = K3bDataDoc::CONTINUE; + } + + else if( info.empty() ) { + // + // We only close the CD if the project fills up the CD almost completely (90%) + // + if( d->doc->size() >= info.capacity().mode1Bytes()*9/10 || + d->doc->writingMode() == K3b::DAO ) + d->usedMultiSessionMode = K3bDataDoc::NONE; + else + d->usedMultiSessionMode = K3bDataDoc::START; + } + + else { // complete (WE SHOULD ACTUALLY NEVER GET HERE SINCE WE WAIT FOR AN EMPTY/APPENDABLE CD ABOVE!) + // + // Now we decide only based on the project size. + // let's just use a 680 MB CD as our reference + // + if( d->doc->size()/1024/1024 >= 680*9/10 || + d->doc->writingMode() == K3b::DAO ) + d->usedMultiSessionMode = K3bDataDoc::NONE; + else + d->usedMultiSessionMode = K3bDataDoc::START; + } + + return d->usedMultiSessionMode; +} + + +void K3bDataJob::cancelAll() +{ + d->canceled = true; + + m_isoImager->cancel(); + m_msInfoFetcher->cancel(); + if( m_writerJob ) + m_writerJob->cancel(); + if( d->verificationJob ) + d->verificationJob->cancel(); + + d->pipe.close(); + + cleanup(); +} + + +bool K3bDataJob::waitForMedium() +{ + emit newSubTask( i18n("Waiting for a medium") ); + if( waitForMedia( d->doc->burner(), + d->usedMultiSessionMode == K3bDataDoc::CONTINUE || + d->usedMultiSessionMode == K3bDataDoc::FINISH ? + K3bDevice::STATE_INCOMPLETE : + K3bDevice::STATE_EMPTY, + K3bDevice::MEDIA_WRITABLE_CD ) < 0 ) { + return false; + } + else + return !d->canceled; +} + + +QString K3bDataJob::jobDescription() const +{ + if( d->doc->onlyCreateImages() ) { + return i18n("Creating Data Image File"); + } + else if( d->doc->multiSessionMode() == K3bDataDoc::NONE || + d->doc->multiSessionMode() == K3bDataDoc::AUTO ) { + return i18n("Writing Data CD") + + ( d->doc->isoOptions().volumeID().isEmpty() + ? QString::null + : QString( " (%1)" ).arg(d->doc->isoOptions().volumeID()) ); + } + else { + return i18n("Writing Multisession CD") + + ( d->doc->isoOptions().volumeID().isEmpty() + ? QString::null + : QString( " (%1)" ).arg(d->doc->isoOptions().volumeID()) ); + } +} + + +QString K3bDataJob::jobDetails() const +{ + if( d->doc->copies() > 1 && + !d->doc->dummy() && + !(d->doc->multiSessionMode() == K3bDataDoc::CONTINUE || + d->doc->multiSessionMode() == K3bDataDoc::FINISH) ) + return i18n("ISO9660 Filesystem (Size: %1) - %n copy", + "ISO9660 Filesystem (Size: %1) - %n copies", + d->doc->copies() ) + .arg(KIO::convertSize( d->doc->size() )); + else + return i18n("ISO9660 Filesystem (Size: %1)") + .arg(KIO::convertSize( d->doc->size() )); +} + + +K3bDataDoc::MultiSessionMode K3bDataJob::usedMultiSessionMode() const +{ + return d->usedMultiSessionMode; +} + + +void K3bDataJob::cleanup() +{ + if( !d->doc->onTheFly() && d->doc->removeImages() ) { + if( QFile::exists( d->doc->tempDir() ) ) { + d->imageFile.remove(); + emit infoMessage( i18n("Removed image file %1").arg(d->doc->tempDir()), K3bJob::SUCCESS ); + } + } + + if( d->tocFile ) { + delete d->tocFile; + d->tocFile = 0; + } +} + + +bool K3bDataJob::hasBeenCanceled() const +{ + return d->canceled; +} + +#include "k3bdatajob.moc" diff --git a/libk3b/projects/datacd/k3bdatajob.h b/libk3b/projects/datacd/k3bdatajob.h new file mode 100644 index 0000000..58de969 --- /dev/null +++ b/libk3b/projects/datacd/k3bdatajob.h @@ -0,0 +1,111 @@ +/* + * + * $Id: k3bdatajob.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef K3BDATAJOB_H +#define K3BDATAJOB_H + +#include <k3bjob.h> +#include <k3bdatadoc.h> + +#include <qfile.h> + +class QString; +class QDataStream; +class K3bAbstractWriter; +class K3bIsoImager; +class KTempFile; +class K3bMsInfoFetcher; + +namespace K3bDevice { + class DeviceHandler; + class DiskInfo; +} + +/** + *@author Sebastian Trueg + */ +class K3bDataJob : public K3bBurnJob +{ + Q_OBJECT + + public: + K3bDataJob( K3bDataDoc*, K3bJobHandler*, QObject* parent = 0 ); + virtual ~K3bDataJob(); + + K3bDoc* doc() const; + K3bDevice::Device* writer() const; + + virtual bool hasBeenCanceled() const; + + virtual QString jobDescription() const; + virtual QString jobDetails() const; + + public slots: + void cancel(); + void start(); + + /** + * Used to specify a non-default writer. + * If this does notget called K3bDataJob determines + * the writer itself. + */ + void setWriterJob( K3bAbstractWriter* ); + void setImager( K3bIsoImager* ); + + protected slots: + void slotIsoImagerFinished( bool success ); + void slotIsoImagerPercent(int); + void slotWriterJobPercent( int p ); + void slotWriterNextTrack( int t, int tt ); + void slotWriterJobFinished( bool success ); + void slotVerificationProgress( int ); + void slotVerificationFinished( bool ); + void slotMsInfoFetched(bool); + void slotDetermineMultiSessionMode( K3bDevice::DeviceHandler* dh ); + void writeImage(); + void cancelAll(); + + /** + * Just a little helper method that makes subclassing easier. + * Basically used for DVD writing. + */ + virtual bool waitForMedium(); + + protected: + virtual void prepareData(); + virtual bool prepareWriterJob(); + virtual void prepareImager(); + virtual void determineMultiSessionMode(); + virtual K3bDataDoc::MultiSessionMode getMultiSessionMode( const K3bDevice::DiskInfo& ); + virtual void cleanup(); + + K3bDataDoc::MultiSessionMode usedMultiSessionMode() const; + + K3bAbstractWriter* m_writerJob; + K3bIsoImager* m_isoImager; + K3bMsInfoFetcher* m_msInfoFetcher; + + private: + bool startWriterJob(); + bool startOnTheFlyWriting(); + void prepareWriting(); + void connectImager(); + + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/projects/datacd/k3bdatapreparationjob.cpp b/libk3b/projects/datacd/k3bdatapreparationjob.cpp new file mode 100644 index 0000000..dd29d6d --- /dev/null +++ b/libk3b/projects/datacd/k3bdatapreparationjob.cpp @@ -0,0 +1,283 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bdatapreparationjob.h" +#include "k3bdatadoc.h" +#include "k3bisooptions.h" + +#include <k3bthreadjob.h> +#include <k3bthread.h> +#include <k3bdiritem.h> +#include <k3bfileitem.h> +#include <k3bglobals.h> + +#include <klocale.h> +#include <kstringhandler.h> + +#include <qfile.h> +#include <qvaluelist.h> + + +class K3bDataPreparationJob::Private : public K3bThread +{ +public: + Private( K3bDataDoc* doc ); + + void run(); + void cancel(); + + K3bDataDoc* doc; + + QValueList<K3bDataItem*> nonExistingItems; + QString listOfRenamedItems; + QValueList<K3bDataItem*> folderSymLinkItems; + + K3bThreadJob* threadJob; + + bool canceled; +}; + + +K3bDataPreparationJob::Private::Private( K3bDataDoc* _doc ) + : doc(_doc) +{ +} + + +void K3bDataPreparationJob::Private::run() +{ + emitStarted(); + + // clean up + nonExistingItems.clear(); + listOfRenamedItems.truncate(0); + folderSymLinkItems.clear(); + + // initialize filenames in the project + doc->prepareFilenames(); + + // create the message string for the renamed files + if( doc->needToCutFilenames() ) { + int maxlines = 10; + QValueList<K3bDataItem*>::const_iterator it; + for( it = doc->needToCutFilenameItems().begin(); + maxlines > 0 && it != doc->needToCutFilenameItems().end(); + ++it, --maxlines ) { + K3bDataItem* item = *it; + listOfRenamedItems += i18n("<em>%1</em> renamed to <em>%2</em>") + .arg( KStringHandler::csqueeze( item->k3bName(), 30 ) ) + .arg( KStringHandler::csqueeze( item->writtenName(), 30 ) ); + listOfRenamedItems += "<br>"; + } + if( it != doc->needToCutFilenameItems().end() ) + listOfRenamedItems += "..."; + } + + // + // Check for missing files and folder symlinks + // + K3bDataItem* item = doc->root(); + while( (item = item->nextSibling()) ) { + + if( item->isSymLink() ) { + if( doc->isoOptions().followSymbolicLinks() ) { + QFileInfo f( K3b::resolveLink( item->localPath() ) ); + if( !f.exists() ) { + nonExistingItems.append( item ); + } + else if( f.isDir() ) { + folderSymLinkItems.append( item ); + } + } + } + else if( item->isFile() && !QFile::exists( item->localPath() ) ) { + nonExistingItems.append( item ); + } + + if( canceled ) { + emitCanceled(); + emitFinished(false); + return; + } + } + + + emitFinished( true ); +} + + +void K3bDataPreparationJob::Private::cancel() +{ + canceled = true; +} + + + + +static QString createItemsString( const QValueList<K3bDataItem*>& items, unsigned int max ) +{ + QString s; + unsigned int cnt = 0; + for( QValueList<K3bDataItem*>::const_iterator it = items.begin(); + it != items.end(); ++it ) { + + s += KStringHandler::csqueeze( (*it)->localPath(), 60 ); + + ++cnt; + if( cnt >= max || it == items.end() ) + break; + + s += "<br>"; + } + + if( items.count() > max ) + s += "..."; + + return s; +} + + +K3bDataPreparationJob::K3bDataPreparationJob( K3bDataDoc* doc, K3bJobHandler* hdl, QObject* parent ) + : K3bJob( hdl, parent ) +{ + d = new Private( doc ); + d->threadJob = new K3bThreadJob( d, this, this ); + connectSubJob( d->threadJob, SLOT(slotWorkDone(bool)), K3bJob::DEFAULT_SIGNAL_CONNECTION ); +} + + +K3bDataPreparationJob::~K3bDataPreparationJob() +{ + delete d; +} + + +void K3bDataPreparationJob::start() +{ + if( !active() ) { + d->canceled = false; + jobStarted(); + d->threadJob->start(); + } +} + + +void K3bDataPreparationJob::slotWorkDone( bool success ) +{ + if( success ) { + if( !d->listOfRenamedItems.isEmpty() ) { + if( !questionYesNo( "<p>" + i18n("Some filenames need to be shortened due to the %1 char restriction " + "of the Joliet extensions. If the Joliet extensions are disabled filenames " + "do not have to be shortened but long filenames will not be available on " + "Windows systems.") + .arg( d->doc->isoOptions().jolietLong() ? 103 : 64 ) + + "<p>" + d->listOfRenamedItems, + i18n("Warning"), + i18n("Shorten Filenames"), + i18n("Disable Joliet extensions") ) ) { + // No -> disable joliet + // for now we enable RockRidge to be sure we did not lie above (keep long filenames) + K3bIsoOptions op = d->doc->isoOptions(); + op.setCreateJoliet( false ); + op.setCreateRockRidge( true ); + d->doc->setIsoOptions( op ); + d->doc->prepareFilenames(); + } + } + + // + // The joliet extension encodes the volume desc in UCS-2, i.e. uses 16 bit for each char. + // Thus, the max length here is 16. + // + if( d->doc->isoOptions().createJoliet() && + d->doc->isoOptions().volumeID().length() > 16 ) { + if( !questionYesNo( "<p>" + i18n("The Joliet extensions (which are needed for long filenames on Windows systems) " + "restrict the length of the volume descriptior (the name of the filesystem) " + "to %1 characters. The selected descriptor '%2' is longer than that. Do you " + "want it to be cut or do you want to go back and change it manually?") + .arg( 16 ).arg( d->doc->isoOptions().volumeID() ), + i18n("Warning"), + i18n("Cut volume descriptor in the Joliet tree"), + i18n("Cancel and go back") ) ) { + d->canceled = true; + emit canceled(); + jobFinished( false ); + return; + } + } + + // + // Check for missing files + // + if( !d->nonExistingItems.isEmpty() ) { + if( questionYesNo( "<p>" + i18n("The following files could not be found. Do you want to remove them from the " + "project and continue without adding them to the image?") + + "<p>" + createItemsString( d->nonExistingItems, 10 ), + i18n("Warning"), + i18n("Remove missing files and continue"), + i18n("Cancel and go back") ) ) { + for( QValueList<K3bDataItem*>::const_iterator it = d->nonExistingItems.begin(); + it != d->nonExistingItems.end(); ++it ) { + delete *it; + } + } + else { + d->canceled = true; + emit canceled(); + jobFinished(false); + return; + } + } + + // + // Warn about symlinks to folders + // + if( d->doc->isoOptions().followSymbolicLinks() && !d->folderSymLinkItems.isEmpty() ) { + if( !questionYesNo( "<p>" + i18n("K3b is not able to follow symbolic links to folders after they have been added " + "to the project. Do you want to continue " + "without writing the symbolic links to the image?") + + "<p>" + createItemsString( d->folderSymLinkItems, 10 ), + i18n("Warning"), + i18n("Discard symbolic links to folders"), + i18n("Cancel and go back") ) ) { + d->canceled = true; + emit canceled(); + jobFinished(false); + return; + } + } + + jobFinished( true ); + } + else { + if( d->canceled ) + emit canceled(); + jobFinished(false); + } +} + + +void K3bDataPreparationJob::cancel() +{ + d->cancel(); +} + + +bool K3bDataPreparationJob::hasBeenCanceled() const +{ + return d->canceled; +} + +#include "k3bdatapreparationjob.moc" diff --git a/libk3b/projects/datacd/k3bdatapreparationjob.h b/libk3b/projects/datacd/k3bdatapreparationjob.h new file mode 100644 index 0000000..1c83a42 --- /dev/null +++ b/libk3b/projects/datacd/k3bdatapreparationjob.h @@ -0,0 +1,51 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_DATA_PREPARATION_JOB_H_ +#define _K3B_DATA_PREPARATION_JOB_H_ + +#include <k3bjob.h> + + +class K3bDataDoc; +class K3bJobHandler; + +/** + * The K3bDataPreparationJob performs some checks on the data in a data project + * It is used by th K3bIsoImager. + */ +class K3bDataPreparationJob : public K3bJob +{ + Q_OBJECT + + public: + K3bDataPreparationJob( K3bDataDoc* doc, K3bJobHandler* hdl, QObject* parent ); + ~K3bDataPreparationJob(); + + bool hasBeenCanceled() const; + + public slots: + void start(); + void cancel(); + + private slots: + void slotWorkDone( bool success ); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/libk3b/projects/datacd/k3bdiritem.cpp b/libk3b/projects/datacd/k3bdiritem.cpp new file mode 100644 index 0000000..3ea3236 --- /dev/null +++ b/libk3b/projects/datacd/k3bdiritem.cpp @@ -0,0 +1,406 @@ +/* + * + * $Id: k3bdiritem.cpp 652578 2007-04-11 14:21:04Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3bdiritem.h" +#include "k3bdatadoc.h" +#include "k3bsessionimportitem.h" +#include "k3bfileitem.h" + +#include <qstring.h> +#include <qptrlist.h> + +#include <kdebug.h> + + +K3bDirItem::K3bDirItem(const QString& name, K3bDataDoc* doc, K3bDirItem* parentDir) + : K3bDataItem( doc, parentDir ), + m_size(0), + m_followSymlinksSize(0), + m_blocks(0), + m_followSymlinksBlocks(0), + m_files(0), + m_dirs(0) +{ + m_k3bName = name; + + // add automagically like a qlistviewitem + if( parent() ) + parent()->addDataItem( this ); +} + + +K3bDirItem::K3bDirItem( const K3bDirItem& item ) + : K3bDataItem( item ), + m_size(0), + m_followSymlinksSize(0), + m_blocks(0), + m_followSymlinksBlocks(0), + m_files(0), + m_dirs(0), + m_localPath( item.m_localPath ) +{ + for( QPtrListIterator<K3bDataItem> it( item.children() ); *it; ++it ) + addDataItem( (*it)->copy() ); +} + +K3bDirItem::~K3bDirItem() +{ + // delete all children + // doing this by hand is much saver than using the + // auto-delete feature since some of the items' destructors + // may change the list + K3bDataItem* i = m_children.first(); + while( i ) { + // it is important to use takeDataItem here to be sure + // the size gets updated properly + takeDataItem(i); + delete i; + i = m_children.first(); + } + + // this has to be done after deleting the children + // because the directory itself has a size of 0 in K3b + // and all it's files' sizes have already been substracted + take(); +} + + +K3bDataItem* K3bDirItem::copy() const +{ + return new K3bDirItem( *this ); +} + + +K3bDirItem* K3bDirItem::getDirItem() const +{ + return const_cast<K3bDirItem*>(this); +} + +K3bDirItem* K3bDirItem::addDataItem( K3bDataItem* item ) +{ + // check if we are a subdir of item + if( K3bDirItem* dirItem = dynamic_cast<K3bDirItem*>(item) ) { + if( dirItem->isSubItem( this ) ) { + kdDebug() << "(K3bDirItem) trying to move a dir item down in it's own tree." << endl; + return this; + } + } + + if( m_children.findRef( item ) == -1 ) { + if( item->isFile() ) { + // do we replace an old item? + QString name = item->k3bName(); + int cnt = 1; + while( K3bDataItem* oldItem = find( name ) ) { + if( !oldItem->isDir() && oldItem->isFromOldSession() ) { + // in this case we remove this item from it's parent and save it in the new one + // to be able to recover it + oldItem->take(); + static_cast<K3bSessionImportItem*>(oldItem)->setReplaceItem( static_cast<K3bFileItem*>(item) ); + static_cast<K3bFileItem*>(item)->setReplacedItemFromOldSession( oldItem ); + break; + } + else { + // + // add a counter to the filename + // + if( item->k3bName()[item->k3bName().length()-4] == '.' ) + name = item->k3bName().left( item->k3bName().length()-4 ) + QString::number(cnt++) + item->k3bName().right(4); + else + name = item->k3bName() + QString::number(cnt++); + } + } + item->setK3bName( name ); + } + + m_children.append( item->take() ); + updateSize( item, false ); + if( item->isDir() ) + updateFiles( ((K3bDirItem*)item)->numFiles(), ((K3bDirItem*)item)->numDirs()+1 ); + else + updateFiles( 1, 0 ); + + item->m_parentDir = this; + + // inform the doc + if( doc() ) + doc()->itemAddedToDir( this, item ); + } + + return this; +} + + +K3bDataItem* K3bDirItem::takeDataItem( K3bDataItem* item ) +{ + int x = m_children.findRef( item ); + if( x > -1 ) { + K3bDataItem* item = m_children.take(); + updateSize( item, true ); + if( item->isDir() ) + updateFiles( -1*((K3bDirItem*)item)->numFiles(), -1*((K3bDirItem*)item)->numDirs()-1 ); + else + updateFiles( -1, 0 ); + + item->m_parentDir = 0; + + // inform the doc + if( doc() ) + doc()->itemRemovedFromDir( this, item ); + + if( item->isFile() ) { + // restore the item imported from an old session + if( static_cast<K3bFileItem*>(item)->replaceItemFromOldSession() ) + addDataItem( static_cast<K3bFileItem*>(item)->replaceItemFromOldSession() ); + } + + return item; + } + else + return 0; +} + + +K3bDataItem* K3bDirItem::nextSibling() const +{ + if( !m_children.isEmpty() ) + return m_children.getFirst(); + else + return K3bDataItem::nextSibling(); +} + + +K3bDataItem* K3bDirItem::nextChild( K3bDataItem* prev ) const +{ + // search for prev in children + if( m_children.findRef( prev ) < 0 ) { + return 0; + } + else + return m_children.next(); +} + + +bool K3bDirItem::alreadyInDirectory( const QString& filename ) const +{ + return (find( filename ) != 0); +} + + +K3bDataItem* K3bDirItem::find( const QString& filename ) const +{ + for( QPtrListIterator<K3bDataItem> it( m_children ); it.current(); ++it ) { + if( it.current()->k3bName() == filename ) + return it.current(); + } + return 0; +} + + +K3bDataItem* K3bDirItem::findByPath( const QString& p ) +{ + if( p.isEmpty() || p == "/" ) + return this; + + QString path = p; + if( path.startsWith("/") ) + path = path.mid(1); + int pos = path.find( "/" ); + if( pos < 0 ) + return find( path ); + else { + // do it recursivly + K3bDataItem* item = find( path.left(pos) ); + if( item && item->isDir() ) + return ((K3bDirItem*)item)->findByPath( path.mid( pos+1 ) ); + else + return 0; + } +} + + +bool K3bDirItem::mkdir( const QString& dirPath ) +{ + // + // An absolut path always starts at the root item + // + if( dirPath[0] == '/' ) { + if( parent() ) + return parent()->mkdir( dirPath ); + else + return mkdir( dirPath.mid( 1 ) ); + } + + if( findByPath( dirPath ) ) + return false; + + QString restPath; + QString dirName; + int pos = dirPath.find( '/' ); + if( pos == -1 ) { + dirName = dirPath; + } + else { + dirName = dirPath.left( pos ); + restPath = dirPath.mid( pos+1 ); + } + + K3bDataItem* dir = find( dirName ); + if( !dir ) + dir = new K3bDirItem( dirName, doc(), this ); + else if( !dir->isDir() ) + return false; + + if( !restPath.isEmpty() ) + return static_cast<K3bDirItem*>(dir)->mkdir( restPath ); + + return true; +} + + +KIO::filesize_t K3bDirItem::itemSize( bool followsylinks ) const +{ + if( followsylinks ) + return m_followSymlinksSize; + else + return m_size; +} + + +K3b::Msf K3bDirItem::itemBlocks( bool followSymlinks ) const +{ + if( followSymlinks ) + return m_followSymlinksBlocks; + else + return m_blocks; +} + + +bool K3bDirItem::isSubItem( K3bDataItem* item ) const +{ + if( dynamic_cast<K3bDirItem*>(item) == this ) + return true; + + K3bDirItem* d = item->parent(); + while( d ) { + if( d == this ) { + return true; + } + d = d->parent(); + } + + return false; +} + + +long K3bDirItem::numFiles() const +{ + return m_files; +} + + +long K3bDirItem::numDirs() const +{ + return m_dirs; +} + + +bool K3bDirItem::isRemoveable() const +{ + if( !K3bDataItem::isRemoveable() ) + return false; + + for( QPtrListIterator<K3bDataItem> it( m_children ); it.current(); ++it ) { + if( !it.current()->isRemoveable() ) + return false; + } + + return true; +} + + +void K3bDirItem::updateSize( K3bDataItem* item, bool removed ) +{ + if ( !item->isFromOldSession() ) { + if( removed ) { + m_followSymlinksSize -= item->itemSize( true ); + m_size -= item->itemSize( false ); + m_followSymlinksBlocks -= item->itemBlocks( true ).lba(); + m_blocks -= item->itemBlocks( false ).lba(); + } + else { + m_followSymlinksSize += item->itemSize( true ); + m_size += item->itemSize( false ); + m_followSymlinksBlocks += item->itemBlocks( true ).lba(); + m_blocks += item->itemBlocks( false ).lba(); + } + } + + if( parent() ) + parent()->updateSize( item, removed ); +} + +void K3bDirItem::updateFiles( long files, long dirs ) +{ + m_files += files; + m_dirs += dirs; + if( parent() ) + parent()->updateFiles( files, dirs ); +} + + +bool K3bDirItem::isFromOldSession() const +{ + for( QPtrListIterator<K3bDataItem> it( m_children ); it.current(); ++it ) { + if( (*it)->isFromOldSession() ) + return true; + } + return false; +} + + +bool K3bDirItem::writeToCd() const +{ + // check if this dir contains items to write + for( QPtrListIterator<K3bDataItem> it( m_children ); it.current(); ++it ) { + if( (*it)->writeToCd() ) + return true; + } + return K3bDataItem::writeToCd(); +} + + +K3bRootItem::K3bRootItem( K3bDataDoc* doc ) + : K3bDirItem( "root", doc, 0 ) +{ +} + + +K3bRootItem::~K3bRootItem() +{ +} + + +const QString& K3bRootItem::k3bName() const +{ + return doc()->isoOptions().volumeID(); +} + + +void K3bRootItem::setK3bName( const QString& text ) +{ + doc()->setVolumeID( text ); +} diff --git a/libk3b/projects/datacd/k3bdiritem.h b/libk3b/projects/datacd/k3bdiritem.h new file mode 100644 index 0000000..a64b4fd --- /dev/null +++ b/libk3b/projects/datacd/k3bdiritem.h @@ -0,0 +1,155 @@ +/* + * + * $Id: k3bdiritem.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef K3BDIRITEM_H +#define K3BDIRITEM_H + + +#include <qstring.h> +#include <qptrlist.h> + +#include <kio/global.h> + +#include "k3bdataitem.h" +#include "k3b_export.h" +class K3bDataDoc; + +/** + *@author Sebastian Trueg + */ + +class LIBK3B_EXPORT K3bDirItem : public K3bDataItem +{ + public: + K3bDirItem( const QString& name, K3bDataDoc*, K3bDirItem* parentDir = 0 ); + + /** + * Default copy constructor. Copies the dir including all children. However, none of the + * children will have set a doc and the copy dir will not have set a parent dir. + */ + K3bDirItem( const K3bDirItem& ); + + virtual ~K3bDirItem(); + + K3bDataItem* copy() const; + + K3bDirItem* getDirItem() const; + + const QPtrList<K3bDataItem>& children() const { return m_children; } + K3bDirItem* addDataItem( K3bDataItem* item ); + K3bDataItem* takeDataItem( K3bDataItem* item ); + + K3bDataItem* nextSibling() const; + K3bDataItem* nextChild( K3bDataItem* ) const; + + bool alreadyInDirectory( const QString& fileName ) const; + K3bDataItem* find( const QString& filename ) const; + K3bDataItem* findByPath( const QString& ); + + long numFiles() const; + long numDirs() const; + + bool isEmpty() const { return ( numDirs() + numFiles() == 0 ); } + + /** + * returns true if item is a subItem of + * this dir item + * (returns also true if item == this + */ + bool isSubItem( K3bDataItem* item ) const; + + bool isDir() const { return true; } + + virtual bool isRemoveable() const; + + /** + * \return true if some child is from an old session. + */ + virtual bool isFromOldSession() const; + + /** + * Recursively creates a directory. + */ + bool mkdir( const QString& dir ); + + void setLocalPath( const QString& p ) { m_localPath = p; } + QString localPath() const { return m_localPath; } + + /** + * \reimplemented + */ + bool writeToCd() const; + + protected: + /** + * Normally one does not use this method but K3bDataItem::size() + * + * This method does not take into account the possibility to share the data + * between files with the same inode in an iso9660 filesystem. + * For that one has to use K3bFileCompilationSizeHandler. + */ + KIO::filesize_t itemSize( bool followSymlinks ) const; + + /* + * Normally one does not use this method but K3bDataItem::blocks() + */ + K3b::Msf itemBlocks( bool followSymlinks ) const; + + private: + /** + * this recursivly updates the size of the directories. + * The size of this dir and the parent dir is updated. + * These values are just used for user information. + */ + void updateSize( K3bDataItem*, bool removed = false ); + /** + * Updates the number of files and directories. These values are + * just used for user information. + */ + void updateFiles( long files, long dirs ); + + mutable QPtrList<K3bDataItem> m_children; + + // size of the items simply added + KIO::filesize_t m_size; + KIO::filesize_t m_followSymlinksSize; + + // number of blocks (2048 bytes) used by all the items + long m_blocks; + long m_followSymlinksBlocks; + + long m_files; + long m_dirs; + + // HACK: store the original path to be able to use it's permissions + // ´remove this once we have a backup project + QString m_localPath; +}; + + +class K3bRootItem : public K3bDirItem +{ + public: + K3bRootItem( K3bDataDoc* ); + ~K3bRootItem(); + + const QString& k3bName() const; + void setK3bName( const QString& ); + + bool isMoveable() const { return false; } + bool isRemoveable() const { return false; } +}; +#endif diff --git a/libk3b/projects/datacd/k3bfilecompilationsizehandler.cpp b/libk3b/projects/datacd/k3bfilecompilationsizehandler.cpp new file mode 100644 index 0000000..0ddab76 --- /dev/null +++ b/libk3b/projects/datacd/k3bfilecompilationsizehandler.cpp @@ -0,0 +1,228 @@ +/* + * + * $Id: k3bfilecompilationsizehandler.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bfilecompilationsizehandler.h" +#include "k3bfileitem.h" + +#include <kdebug.h> + +#include <qfile.h> +#include <qmap.h> +#include <qptrlist.h> + + +// TODO: remove the items from the project if the savedSize differs +// with some info-widget: "Files xxx have changed on disk. Removing them from the project." +// or we just update the sizes! + + +static long usedBlocks( const KIO::filesize_t& bytes ) +{ + if( bytes % 2048 ) + return bytes/2048 + 1; + else + return bytes/2048; +} + + +class InodeInfo +{ +public: + InodeInfo() { + number = 0; + savedSize = 0; + } + + /** + * How often has the file with + * the corresponding inode been added + */ + int number; + + /** + * The size of the first added file. This has to be saved + * to check further addings and to avoid the following situation: + * A file with inode 1 is added, then deleted. Another file is created + * at inode 1 and added to the project. Now the first file gets + * removed and then the second. If we had not saved the size we would + * have added the size of the first and removed the size of the second + * file resulting in a corrupted project size. + * This way we always use the size of the first added file and may + * warn the user if sizes differ. + */ + KIO::filesize_t savedSize; + + KIO::filesize_t completeSize() const { return savedSize*number; } + + /** + * In an iso9660 filesystem a file occupies complete blocks of 2048 bytes. + */ + K3b::Msf blocks() const { return K3b::Msf( usedBlocks(savedSize) ); } + + QPtrList<K3bDataItem> items; +}; + + +class K3bFileCompilationSizeHandler::Private +{ +public: + Private() + : size(0) { + } + + void clear() { + inodeMap.clear(); + size = 0; + blocks = 0; + } + + void addFile( K3bFileItem* item, bool followSymlinks ) { + InodeInfo& inodeInfo = inodeMap[item->localId(followSymlinks)]; + + inodeInfo.items.append( item ); + + if( inodeInfo.number == 0 ) { + inodeInfo.savedSize = item->itemSize( followSymlinks ); + + size += inodeInfo.savedSize; + blocks += inodeInfo.blocks(); + } + + inodeInfo.number++; + } + + void addSpecialItem( K3bDataItem* item ) { + // special files do not have a corresponding local file + // so we just add their k3bSize + size += item->size(); + blocks += usedBlocks(item->size()); + specialItems.append( item ); + } + + void removeFile( K3bFileItem* item, bool followSymlinks ) { + InodeInfo& inodeInfo = inodeMap[item->localId(followSymlinks)]; + + if( inodeInfo.items.findRef( item ) == -1 ) { + kdError() << "(K3bFileCompilationSizeHandler) " + << item->localPath() + << " has been removed without being added!" << endl; + } + else { + if( item->itemSize(followSymlinks) != inodeInfo.savedSize ) { + kdError() << "(K3bFileCompilationSizeHandler) savedSize differs!" << endl; + } + + inodeInfo.items.removeRef( item ); + inodeInfo.number--; + if( inodeInfo.number == 0 ) { + size -= inodeInfo.savedSize; + blocks -= inodeInfo.blocks(); + } + } + } + + void removeSpecialItem( K3bDataItem* item ) { + // special files do not have a corresponding local file + // so we just substract their k3bSize + if( specialItems.findRef( item ) == -1 ) { + kdError() << "(K3bFileCompilationSizeHandler) Special item " + << item->k3bName() + << " has been removed without being added!" << endl; + } + else { + specialItems.removeRef( item ); + size -= item->size(); + blocks -= usedBlocks(item->size()); + } + } + + + /** + * This maps from inodes to the number of occurrences of the inode. + */ + QMap<K3bFileItem::Id, InodeInfo> inodeMap; + + KIO::filesize_t size; + K3b::Msf blocks; + + QPtrList<K3bDataItem> specialItems; +}; + + + +K3bFileCompilationSizeHandler::K3bFileCompilationSizeHandler() +{ + d_symlinks = new Private; + d_noSymlinks = new Private; +} + +K3bFileCompilationSizeHandler::~K3bFileCompilationSizeHandler() +{ + delete d_symlinks; + delete d_noSymlinks; +} + + +const KIO::filesize_t& K3bFileCompilationSizeHandler::size( bool followSymlinks ) const +{ + if( followSymlinks ) + return d_noSymlinks->size; + else + return d_symlinks->size; +} + + +const K3b::Msf& K3bFileCompilationSizeHandler::blocks( bool followSymlinks ) const +{ + if( followSymlinks ) + return d_noSymlinks->blocks; + else + return d_symlinks->blocks; +} + + +void K3bFileCompilationSizeHandler::addFile( K3bDataItem* item ) +{ + if( item->isSpecialFile() ) { + d_symlinks->addSpecialItem( item ); + d_noSymlinks->addSpecialItem( item ); + } + else if( item->isFile() ) { + K3bFileItem* fileItem = static_cast<K3bFileItem*>( item ); + d_symlinks->addFile( fileItem, false ); + d_noSymlinks->addFile( fileItem, true ); + } +} + + +void K3bFileCompilationSizeHandler::removeFile( K3bDataItem* item ) +{ + if( item->isSpecialFile() ) { + d_symlinks->removeSpecialItem( item ); + d_noSymlinks->removeSpecialItem( item ); + } + else if( item->isFile() ) { + K3bFileItem* fileItem = static_cast<K3bFileItem*>( item ); + d_symlinks->removeFile( fileItem, false ); + d_noSymlinks->removeFile( fileItem, true ); + } +} + + +void K3bFileCompilationSizeHandler::clear() +{ + d_symlinks->clear(); + d_noSymlinks->clear(); +} diff --git a/libk3b/projects/datacd/k3bfilecompilationsizehandler.h b/libk3b/projects/datacd/k3bfilecompilationsizehandler.h new file mode 100644 index 0000000..c996657 --- /dev/null +++ b/libk3b/projects/datacd/k3bfilecompilationsizehandler.h @@ -0,0 +1,73 @@ +/* + * + * $Id: k3bfilecompilationsizehandler.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_FILECOMPILATION_SIZE_HANDLER_H_ +#define _K3B_FILECOMPILATION_SIZE_HANDLER_H_ + + +#include <kio/global.h> +#include <k3bmsf.h> + +class K3bDataItem; + + +/** + * This class maintains a map of indoes and the number + * of files in the doc that belong to that inode. + * This way a more accurate size calculation is possible + * + * It has to be noted that the sizes of the directories + * are only locally true. That means that in some cases + * the root directory of the project may show a much + * higher size than calculated by this class. + */ +class K3bFileCompilationSizeHandler +{ + public: + K3bFileCompilationSizeHandler(); + ~K3bFileCompilationSizeHandler(); + + /** + * This does NOT equal blocks() * 2048. + * This is the sum of the actual file sizes. + */ + const KIO::filesize_t& size( bool followSymlinks = false ) const; + + /** + * Number of blocks the files will occupy. + */ + const K3b::Msf& blocks( bool followSymlinks = false ) const; + + /** + * This will increase the counter for the inode of + * the file in url and update the totel size. + */ + void addFile( K3bDataItem* ); + + /** + * This will decrease the counter for the inode of + * the file in url and update the totel size. + */ + void removeFile( K3bDataItem* ); + + void clear(); + + private: + class Private; + Private* d_symlinks; + Private* d_noSymlinks; +}; + +#endif diff --git a/libk3b/projects/datacd/k3bfileitem.cpp b/libk3b/projects/datacd/k3bfileitem.cpp new file mode 100644 index 0000000..d9e288f --- /dev/null +++ b/libk3b/projects/datacd/k3bfileitem.cpp @@ -0,0 +1,300 @@ +/* + * + * $Id: k3bfileitem.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> +#include <k3bglobals.h> + +#include "k3bfileitem.h" +#include "k3bdatadoc.h" +#include "k3bdiritem.h" +#include "k3bisooptions.h" +#include <k3bglobals.h> + +#include <qfileinfo.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qregexp.h> +#include <qfile.h> + +#include <kurl.h> +#include <kdebug.h> + +#include <errno.h> +#include <string.h> + + +bool operator==( const K3bFileItem::Id& id1, const K3bFileItem::Id& id2 ) +{ + return ( id1.device == id2.device && id1.inode == id2.inode ); +} + + +bool operator<( const K3bFileItem::Id& id1, const K3bFileItem::Id& id2 ) +{ + if( id1.device == id2.device ) + return ( id1.inode < id2.inode ); + else + return ( id1.device < id2.device ); +} + + +bool operator>( const K3bFileItem::Id& id1, const K3bFileItem::Id& id2 ) +{ + return !( id2 < id1 || id1 == id2 ); +} + + + +K3bFileItem::K3bFileItem( const QString& filePath, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName, int flags ) + : K3bDataItem( doc, dir, flags ), + m_replacedItemFromOldSession(0), + m_localPath(filePath) +{ + if( k3bName.isEmpty() ) + m_k3bName = filePath.section( '/', -1 ); + else + m_k3bName = k3bName; + + // we determine the size here to avoid problems with removed or renamed files + // we need to use lstat here since for symlinks both KDE and QT return the size of the file pointed to + // instead the size of the link. + k3b_struct_stat statBuf; + if( k3b_lstat( QFile::encodeName(filePath), &statBuf ) ) { + m_size = K3b::filesize( filePath ); + m_id.inode = 0; + m_id.device = 0; + m_bSymLink = false; + + kdError() << "(KFileItem) lstat failed: " << strerror(errno) << endl; + + // since we have no proper inode info, disable the inode caching in the doc + if( doc ) { + K3bIsoOptions o( doc->isoOptions() ); + o.setDoNotCacheInodes( true ); + doc->setIsoOptions( o ); + } + } + else { + m_size = (KIO::filesize_t)statBuf.st_size; + + m_bSymLink = S_ISLNK(statBuf.st_mode); + + // + // integrate the device number into the inode since files on different + // devices may have the same inode number! + // + m_id.inode = statBuf.st_ino; + m_id.device = statBuf.st_dev; + } + + m_idFollowed = m_id; + m_sizeFollowed = m_size; + + if( isSymLink() ) { + k3b_struct_stat statBuf; + if( k3b_stat( QFile::encodeName(filePath), &statBuf ) == 0 ) { + m_idFollowed.inode = statBuf.st_ino; + m_idFollowed.device = statBuf.st_dev; + + m_sizeFollowed = (KIO::filesize_t)statBuf.st_size; + } + } + + // add automagically like a qlistviewitem + if( parent() ) + parent()->addDataItem( this ); +} + + +K3bFileItem::K3bFileItem( const k3b_struct_stat* stat, + const k3b_struct_stat* followedStat, + const QString& filePath, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName ) + : K3bDataItem( doc, dir ), + m_replacedItemFromOldSession(0), + m_localPath(filePath) +{ + if( k3bName.isEmpty() ) + m_k3bName = filePath.section( '/', -1 ); + else + m_k3bName = k3bName; + + m_size = (KIO::filesize_t)stat->st_size; + m_bSymLink = S_ISLNK(stat->st_mode); + + // + // integrate the device number into the inode since files on different + // devices may have the same inode number! + // + m_id.inode = stat->st_ino; + m_id.device = stat->st_dev; + + if( isSymLink() ) { + m_idFollowed.inode = followedStat->st_ino; + m_idFollowed.device = followedStat->st_dev; + + m_sizeFollowed = (KIO::filesize_t)followedStat->st_size; + } + else { + m_idFollowed = m_id; + m_sizeFollowed = m_size; + } + + if( parent() ) + parent()->addDataItem( this ); +} + + +K3bFileItem::K3bFileItem( const K3bFileItem& item ) + : K3bDataItem( item ), + m_replacedItemFromOldSession(0), + m_size( item.m_size ), + m_sizeFollowed( item.m_sizeFollowed ), + m_id( item.m_id ), + m_idFollowed( item.m_idFollowed ), + m_localPath( item.m_localPath ), + m_bSymLink( item.m_bSymLink ) +{ +} + + +K3bFileItem::~K3bFileItem() +{ + // remove this from parentdir + take(); +} + + +K3bDataItem* K3bFileItem::copy() const +{ + return new K3bFileItem( *this ); +} + + +KIO::filesize_t K3bFileItem::itemSize( bool followSymlinks ) const +{ + if( followSymlinks ) + return m_sizeFollowed; + else + return m_size; +} + + +K3bFileItem::Id K3bFileItem::localId() const +{ + return localId( doc() ? doc()->isoOptions().followSymbolicLinks() || !doc()->isoOptions().createRockRidge() : false ); +} + + +K3bFileItem::Id K3bFileItem::localId( bool followSymlinks ) const +{ + if( followSymlinks ) + return m_idFollowed; + else + return m_id; +} + + +bool K3bFileItem::exists() const +{ + return true; +} + +QString K3bFileItem::absIsoPath() +{ + // return m_dir->absIsoPath() + m_isoName; + return QString::null; +} + + +QString K3bFileItem::localPath() const +{ + return m_localPath; +} + +K3bDirItem* K3bFileItem::getDirItem() const +{ + return getParent(); +} + + +bool K3bFileItem::isSymLink() const +{ + return m_bSymLink; +} + + +QString K3bFileItem::linkDest() const +{ + return QFileInfo( localPath() ).readLink(); +} + + +bool K3bFileItem::isValid() const +{ + if( isSymLink() ) { + + // this link is not valid if we cannot follow it if we want to + if( doc()->isoOptions().followSymbolicLinks() ) { + return QFile::exists( K3b::resolveLink( localPath() ) ); + } + + QString dest = linkDest(); + + if( dest[0] == '/' ) + return false; // absolut links can never be part of the compilation! + + // parse the link + K3bDirItem* dir = getParent(); + + QStringList tokens = QStringList::split( QRegExp("/+"), dest ); // two slashes or more do the same as one does! + + unsigned int i = 0; + while( i < tokens.size() ) { + if( tokens[i] == "." ) { + // ignore it + } + else if( tokens[i] == ".." ) { + // change the directory + dir = dir->parent(); + if( dir == 0 ) + return false; + } + else { + // search for the item in dir + K3bDataItem* d = dir->find( tokens[i] ); + if( d == 0 ) + return false; + + if( d->isDir() ) { + // change directory + dir = (K3bDirItem*)d; + } + else { + if( i+1 != tokens.size() ) + return false; // if di is a file we need to be at the last token + else + return (dest[dest.length()-1] != '/'); // if the link destination ends with a slash + // it can only point to a directory! + } + } + + i++; + } + + return true; + } + else + return true; +} diff --git a/libk3b/projects/datacd/k3bfileitem.h b/libk3b/projects/datacd/k3bfileitem.h new file mode 100644 index 0000000..f23644f --- /dev/null +++ b/libk3b/projects/datacd/k3bfileitem.h @@ -0,0 +1,124 @@ +/* + * + * $Id: k3bfileitem.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef K3BFILEITEM_H +#define K3BFILEITEM_H + + +#include "k3bdataitem.h" +#include <k3bglobals.h> + +#include <kio/global.h> +#include <qstring.h> + +#include "k3b_export.h" + +class K3bDataDoc; +class K3bDirItem; + + +/** + *@author Sebastian Trueg + */ +class LIBK3B_EXPORT K3bFileItem : public K3bDataItem +{ +public: + /** + * Creates a new K3bFileItem + */ + K3bFileItem( const QString& fileName, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName = 0, int flags = 0 ); + + /** + * Constructor for optimized file item creation which does no additional stat. + * + * Used by K3b to speedup file item creation. + */ + K3bFileItem( const k3b_struct_stat* stat, + const k3b_struct_stat* followedStat, + const QString& fileName, K3bDataDoc* doc, K3bDirItem* dir, const QString& k3bName = 0 ); + + /** + * Default copy constructor + * Creates a copy of the fileitem. The copy, however, is not an exact duplicate of this item. + * The copy does not have a parent dir set and any old session items are set to 0. + */ + K3bFileItem( const K3bFileItem& ); + + virtual ~K3bFileItem(); + + virtual K3bDataItem* copy() const; + + bool exists() const; + + QString absIsoPath(); + + /** reimplemented from K3bDataItem */ + QString localPath() const; + + /** + * Identification of the files on the local device. + */ + struct Id { + dev_t device; + ino_t inode; + }; + + /** + * This is not the normal inode number but it also contains + * the device number. + */ + Id localId() const; + + /** + * The id of the file the symlink is pointing to + */ + Id localId( bool followSymlinks ) const; + + K3bDirItem* getDirItem() const; + + bool isSymLink() const; + QString linkDest() const; + bool isFile() const { return true; } + + /** returns true if the item is not a link or + * if the link's destination is part of the compilation */ + bool isValid() const; + + K3bDataItem* replaceItemFromOldSession() const { return m_replacedItemFromOldSession; } + void setReplacedItemFromOldSession( K3bDataItem* item ) { m_replacedItemFromOldSession = item; } + + /** + * Normally one does not use this method but K3bDataItem::size() + */ + KIO::filesize_t itemSize( bool followSymlinks ) const; + + private: + K3bDataItem* m_replacedItemFromOldSession; + + KIO::filesize_t m_size; + KIO::filesize_t m_sizeFollowed; + Id m_id; + Id m_idFollowed; + + QString m_localPath; + bool m_bSymLink; +}; + +bool operator==( const K3bFileItem::Id&, const K3bFileItem::Id& ); +bool operator<( const K3bFileItem::Id&, const K3bFileItem::Id& ); +bool operator>( const K3bFileItem::Id&, const K3bFileItem::Id& ); + +#endif diff --git a/libk3b/projects/datacd/k3bisoimager.cpp b/libk3b/projects/datacd/k3bisoimager.cpp new file mode 100644 index 0000000..f44d3ab --- /dev/null +++ b/libk3b/projects/datacd/k3bisoimager.cpp @@ -0,0 +1,1187 @@ +/* + * + * $Id: k3bisoimager.cpp 655085 2007-04-17 17:48:36Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include <config.h> +#include <k3bglobals.h> + +#include "k3bisoimager.h" +#include "k3bdiritem.h" +#include "k3bbootitem.h" +#include "k3bdatadoc.h" +#include "k3bdatapreparationjob.h" +#include <k3bexternalbinmanager.h> +#include <k3bdevice.h> +#include <k3bprocess.h> +#include <k3bcore.h> +#include <k3bversion.h> +#include <k3bglobals.h> +#include <k3bchecksumpipe.h> +#include <k3bfilesplitter.h> + +#include <kdebug.h> +#include <kstandarddirs.h> +#include <klocale.h> +#include <ktempfile.h> +#include <kio/netaccess.h> +#include <kio/global.h> +#include <kio/job.h> +#include <kstringhandler.h> + +#include <qfile.h> +#include <qregexp.h> +#include <qdir.h> +#include <qapplication.h> +#include <qvaluestack.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <utime.h> + + +int K3bIsoImager::s_imagerSessionCounter = 0; + + +class K3bIsoImager::Private +{ +public: + Private() + : pipe(0) { + } + + ~Private() { + delete pipe; + } + + QString imagePath; + K3bFileSplitter imageFile; + const K3bExternalBin* mkisofsBin; + + enum LinkHandling { + KEEP_ALL, + FOLLOW, + DISCARD_ALL, + DISCARD_BROKEN + }; + + int usedLinkHandling; + + bool knownError; + + K3bActivePipe* pipe; + K3bDataPreparationJob* dataPreparationJob; +}; + + +K3bIsoImager::K3bIsoImager( K3bDataDoc* doc, K3bJobHandler* hdl, QObject* parent, const char* name ) + : K3bJob( hdl, parent, name ), + m_pathSpecFile(0), + m_rrHideFile(0), + m_jolietHideFile(0), + m_sortWeightFile(0), + m_process( 0 ), + m_processExited(false), + m_doc( doc ), + m_noDeepDirectoryRelocation( false ), + m_importSession( false ), + m_device(0), + m_mkisofsPrintSizeResult( 0 ), + m_fdToWriteTo(-1) +{ + d = new Private(); + d->dataPreparationJob = new K3bDataPreparationJob( doc, this, this ); + connectSubJob( d->dataPreparationJob, + SLOT(slotDataPreparationDone(bool)), + DEFAULT_SIGNAL_CONNECTION ); +} + + +K3bIsoImager::~K3bIsoImager() +{ + cleanup(); + delete d; +} + + +bool K3bIsoImager::active() const +{ + return K3bJob::active(); +} + + +void K3bIsoImager::writeToFd( int fd ) +{ + m_fdToWriteTo = fd; +} + + +void K3bIsoImager::writeToImageFile( const QString& path ) +{ + d->imagePath = path; + m_fdToWriteTo = -1; +} + + +void K3bIsoImager::slotReceivedStderr( const QString& line ) +{ + parseMkisofsOutput( line ); + emit debuggingOutput( "mkisofs", line ); +} + + +void K3bIsoImager::handleMkisofsProgress( int p ) +{ + emit percent( p ); +} + + +void K3bIsoImager::handleMkisofsInfoMessage( const QString& line, int type ) +{ + emit infoMessage( line, type ); + if( type == ERROR ) + d->knownError = true; +} + + +void K3bIsoImager::slotProcessExited( KProcess* p ) +{ + kdDebug() << k_funcinfo << endl; + + m_processExited = true; + + d->pipe->close(); + + emit debuggingOutput( "K3bIsoImager", + QString("Pipe throughput: %1 bytes read, %2 bytes written.") + .arg(d->pipe->bytesRead()).arg(d->pipe->bytesWritten()) ); + + if( d->imageFile.isOpen() ) { + d->imageFile.close(); + + if( m_canceled || p->exitStatus() != 0 ) { + d->imageFile.remove(); + emit infoMessage( i18n("Removed incomplete image file %1.").arg(d->imageFile.name()), WARNING ); + } + } + + if( m_canceled ) { + emit canceled(); + jobFinished(false); + } + else { + if( p->normalExit() ) { + if( p->exitStatus() == 0 ) { + jobFinished( !mkisofsReadError() ); + } + else { + switch( p->exitStatus() ) { + case 104: + // connection reset by peer + // This only happens if cdrecord does not finish successfully + // so we may leave the error handling to it meaning we handle this + // as a known error + break; + + case 2: + // mkisofs seems to have a bug that prevents to use filenames + // that contain one or more backslashes + // mkisofs 1.14 has the bug, 1.15a40 not + // TODO: find out the version that fixed the bug + if( m_containsFilesWithMultibleBackslashes && + !k3bcore->externalBinManager()->binObject( "mkisofs" )->hasFeature( "backslashed_filenames" ) ) { + emit infoMessage( i18n("Due to a bug in mkisofs <= 1.15a40, K3b is unable to handle " + "filenames that contain more than one backslash:"), ERROR ); + + break; + } + // otherwise just fall through + + default: + if( !d->knownError && !mkisofsReadError() ) { + emit infoMessage( i18n("%1 returned an unknown error (code %2).").arg("mkisofs").arg(p->exitStatus()), + K3bJob::ERROR ); + emit infoMessage( i18n("Please send me an email with the last output."), K3bJob::ERROR ); + } + } + + jobFinished( false ); + } + } + else { + emit infoMessage( i18n("%1 did not exit cleanly.").arg("mkisofs"), ERROR ); + jobFinished( false ); + } + } + + cleanup(); +} + + +void K3bIsoImager::cleanup() +{ + // remove all temp files + delete m_pathSpecFile; + delete m_rrHideFile; + delete m_jolietHideFile; + delete m_sortWeightFile; + + // remove boot-images-temp files + for( QStringList::iterator it = m_tempFiles.begin(); + it != m_tempFiles.end(); ++it ) + QFile::remove( *it ); + m_tempFiles.clear(); + + m_pathSpecFile = m_jolietHideFile = m_rrHideFile = m_sortWeightFile = 0; + + delete m_process; + m_process = 0; + + clearDummyDirs(); +} + + +void K3bIsoImager::init() +{ + jobStarted(); + + cleanup(); + + d->dataPreparationJob->start(); +} + + +void K3bIsoImager::slotDataPreparationDone( bool success ) +{ + if( success ) { + // + // We always calculate the image size. It does not take long and at least the mixed job needs it + // anyway + // + startSizeCalculation(); + } + else { + if( d->dataPreparationJob->hasBeenCanceled() ) { + m_canceled = true; + emit canceled(); + } + jobFinished( false ); + } +} + + +void K3bIsoImager::calculateSize() +{ + jobStarted(); + startSizeCalculation(); +} + + +void K3bIsoImager::startSizeCalculation() +{ + d->mkisofsBin = initMkisofs(); + if( !d->mkisofsBin ) { + jobFinished( false ); + return; + } + + initVariables(); + + delete m_process; + m_process = new K3bProcess(); + m_process->setRunPrivileged(true); + m_process->setSplitStdout(true); + + emit debuggingOutput( "Used versions", "mkisofs: " + d->mkisofsBin->version ); + + *m_process << d->mkisofsBin; + + if( !prepareMkisofsFiles() || + !addMkisofsParameters(true) ) { + cleanup(); + jobFinished( false ); + return; + } + + // add empty dummy dir since one path-spec is needed + // ??? Seems it is not needed after all. At least mkisofs 1.14 and above don't need it. ??? + // *m_process << dummyDir(); + + kdDebug() << "***** mkisofs calculate size parameters:\n"; + const QValueList<QCString>& args = m_process->args(); + QString s; + for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) { + s += *it + " "; + } + kdDebug() << s << endl << flush; + emit debuggingOutput("mkisofs calculate size command:", s); + + // since output changed during mkisofs version changes we grab both + // stdout and stderr + + // mkisofs version >= 1.15 (don't know about 1.14!) + // the extends on stdout (as lonely number) + // and error and warning messages on stderr + + // mkisofs >= 1.13 + // everything is written to stderr + // last line is: "Total extents scheduled to be written = XXXXX" + + // TODO: use K3bProcess::OutputCollector instead iof our own two slots. + + connect( m_process, SIGNAL(receivedStderr(KProcess*, char*, int)), + this, SLOT(slotCollectMkisofsPrintSizeStderr(KProcess*, char*, int)) ); + connect( m_process, SIGNAL(stdoutLine(const QString&)), + this, SLOT(slotCollectMkisofsPrintSizeStdout(const QString&)) ); + connect( m_process, SIGNAL(processExited(KProcess*)), + this, SLOT(slotMkisofsPrintSizeFinished()) ); + + // we also want error messages + connect( m_process, SIGNAL(stderrLine( const QString& )), + this, SLOT(slotReceivedStderr( const QString& )) ); + + m_collectedMkisofsPrintSizeStdout = QString::null; + m_collectedMkisofsPrintSizeStderr = QString::null; + m_mkisofsPrintSizeResult = 0; + + if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) { + emit infoMessage( i18n("Could not start %1.").arg("mkisofs"), K3bJob::ERROR ); + cleanup(); + + jobFinished( false ); + return; + } +} + + +void K3bIsoImager::slotCollectMkisofsPrintSizeStderr(KProcess*, char* data , int len) +{ + emit debuggingOutput( "mkisofs", QString::fromLocal8Bit( data, len ) ); + m_collectedMkisofsPrintSizeStderr.append( QString::fromLocal8Bit( data, len ) ); +} + + +void K3bIsoImager::slotCollectMkisofsPrintSizeStdout( const QString& line ) +{ + // newer versions of mkisofs outut additional lines of junk before the size :( + // so we only use the last line + emit debuggingOutput( "mkisofs", line ); + m_collectedMkisofsPrintSizeStdout = line; +} + + +void K3bIsoImager::slotMkisofsPrintSizeFinished() +{ + if( m_canceled ) { + emit canceled(); + jobFinished( false ); + return; + } + + bool success = true; + + // if m_collectedMkisofsPrintSizeStdout is not empty we have a recent version of + // mkisofs and parsing is very easy (s.o.) + if( !m_collectedMkisofsPrintSizeStdout.isEmpty() ) { + kdDebug() << "(K3bIsoImager) iso size: " << m_collectedMkisofsPrintSizeStdout << endl; + m_mkisofsPrintSizeResult = m_collectedMkisofsPrintSizeStdout.toInt( &success ); + } + else { + // parse the stderr output + // I hope parsing the last line is enough! + int pos = m_collectedMkisofsPrintSizeStderr.findRev( "extents scheduled to be written" ); + + if( pos == -1 ) + success = false; + else + m_mkisofsPrintSizeResult = m_collectedMkisofsPrintSizeStderr.mid( pos+33 ).toInt( &success ); + } + + emit debuggingOutput( "K3bIsoImager", + QString("mkisofs print size result: %1 (%2 bytes)") + .arg(m_mkisofsPrintSizeResult) + .arg(Q_UINT64(m_mkisofsPrintSizeResult)*2048ULL) ); + + cleanup(); + + + if( success ) { + jobFinished( true ); + } + else { + m_mkisofsPrintSizeResult = 0; + kdDebug() << "(K3bIsoImager) Parsing mkisofs -print-size failed: " << m_collectedMkisofsPrintSizeStdout << endl; + emit infoMessage( i18n("Could not determine size of resulting image file."), ERROR ); + jobFinished( false ); + } +} + + +void K3bIsoImager::initVariables() +{ + m_containsFilesWithMultibleBackslashes = false; + m_processExited = false; + m_canceled = false; + d->knownError = false; + + // determine symlink handling + // follow links superseeds discard all links which superseeds discard broken links + // without rockridge we follow the links or discard all + if( m_doc->isoOptions().followSymbolicLinks() ) + d->usedLinkHandling = Private::FOLLOW; + else if( m_doc->isoOptions().discardSymlinks() ) + d->usedLinkHandling = Private::DISCARD_ALL; + else if( m_doc->isoOptions().createRockRidge() ) { + if( m_doc->isoOptions().discardBrokenSymlinks() ) + d->usedLinkHandling = Private::DISCARD_BROKEN; + else + d->usedLinkHandling = Private::KEEP_ALL; + } + else { + d->usedLinkHandling = Private::FOLLOW; + } + + m_sessionNumber = s_imagerSessionCounter++; +} + + +void K3bIsoImager::start() +{ + jobStarted(); + + cleanup(); + + d->mkisofsBin = initMkisofs(); + if( !d->mkisofsBin ) { + jobFinished( false ); + return; + } + + initVariables(); + + m_process = new K3bProcess(); + m_process->setRunPrivileged(true); + + *m_process << d->mkisofsBin; + + // prepare the filenames as written to the image + m_doc->prepareFilenames(); + + if( !prepareMkisofsFiles() || + !addMkisofsParameters() ) { + cleanup(); + jobFinished( false ); + return; + } + + connect( m_process, SIGNAL(processExited(KProcess*)), + this, SLOT(slotProcessExited(KProcess*)) ); + + connect( m_process, SIGNAL(stderrLine( const QString& )), + this, SLOT(slotReceivedStderr( const QString& )) ); + + // + // Check the image file + if( m_fdToWriteTo == -1 ) { + d->imageFile.setName( d->imagePath ); + if( !d->imageFile.open( IO_WriteOnly ) ) { + emit infoMessage( i18n("Could not open %1 for writing").arg(d->imagePath), ERROR ); + cleanup(); + jobFinished(false); + return; + } + } + + // + // Open the active pipe which does the streaming + delete d->pipe; + if( m_doc->verifyData() ) + d->pipe = new K3bChecksumPipe(); + else + d->pipe = new K3bActivePipe(); + + if( m_fdToWriteTo == -1 ) + d->pipe->writeToIODevice( &d->imageFile ); + else + d->pipe->writeToFd( m_fdToWriteTo ); + d->pipe->open(); + m_process->writeToFd( d->pipe->in() ); + + + kdDebug() << "***** mkisofs parameters:\n"; + const QValueList<QCString>& args = m_process->args(); + QString s; + for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) { + s += *it + " "; + } + kdDebug() << s << endl << flush; + emit debuggingOutput("mkisofs command:", s); + + if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput) ) { + // something went wrong when starting the program + // it "should" be the executable + kdDebug() << "(K3bIsoImager) could not start mkisofs" << endl; + emit infoMessage( i18n("Could not start %1.").arg("mkisofs"), K3bJob::ERROR ); + jobFinished( false ); + cleanup(); + } +} + + +void K3bIsoImager::cancel() +{ + m_canceled = true; + + if( m_process && !m_processExited ) { + m_process->kill(); + } + else if( active() ) { + emit canceled(); + jobFinished(false); + } +} + + +void K3bIsoImager::setMultiSessionInfo( const QString& info, K3bDevice::Device* dev ) +{ + m_multiSessionInfo = info; + m_device = dev; +} + + +// iso9660 + RR use some latin1 variant. So we need to cut the desc fields +// counting 8bit chars. The GUI should take care of restricting the length +// and the charset +static void truncateTheHardWay( QString& s, int max ) +{ + QCString cs = s.utf8(); + cs.truncate(max); + s = QString::fromUtf8( cs ); +} + + +bool K3bIsoImager::addMkisofsParameters( bool printSize ) +{ + // add multisession info + if( !m_multiSessionInfo.isEmpty() ) { + *m_process << "-cdrecord-params" << m_multiSessionInfo; + if( m_device ) + *m_process << "-prev-session" << m_device->blockDeviceName(); + } + + // add the arguments + *m_process << "-gui"; + *m_process << "-graft-points"; + + if( printSize ) + *m_process << "-print-size" << "-quiet"; + + if( !m_doc->isoOptions().volumeID().isEmpty() ) { + QString s = m_doc->isoOptions().volumeID(); + truncateTheHardWay(s, 32); // ensure max length + *m_process << "-volid" << s; + } + else { + emit infoMessage( i18n("No volume id specified. Using default."), WARNING ); + *m_process << "-volid" << "CDROM"; + } + + QString s = m_doc->isoOptions().volumeSetId(); + truncateTheHardWay(s, 128); // ensure max length + *m_process << "-volset" << s; + + s = m_doc->isoOptions().applicationID(); + truncateTheHardWay(s, 128); // ensure max length + *m_process << "-appid" << s; + + s = m_doc->isoOptions().publisher(); + truncateTheHardWay(s, 128); // ensure max length + *m_process << "-publisher" << s; + + s = m_doc->isoOptions().preparer(); + truncateTheHardWay(s, 128); // ensure max length + *m_process << "-preparer" << s; + + s = m_doc->isoOptions().systemId(); + truncateTheHardWay(s, 32); // ensure max length + *m_process << "-sysid" << s; + + s = m_doc->isoOptions().abstractFile(); + truncateTheHardWay(s, 37); // ensure max length + if ( !s.isEmpty() ) + *m_process << "-abstract" << s; + + s = m_doc->isoOptions().copyrightFile(); + truncateTheHardWay(s, 37); // ensure max length + if ( !s.isEmpty() ) + *m_process << "-copyright" << s; + + s = m_doc->isoOptions().bibliographFile(); + truncateTheHardWay(s, 37); // ensure max length + if ( !s.isEmpty() ) + *m_process << "-biblio" << s; + + int volsetSize = m_doc->isoOptions().volumeSetSize(); + int volsetSeqNo = m_doc->isoOptions().volumeSetNumber(); + if( volsetSeqNo > volsetSize ) { + kdDebug() << "(K3bIsoImager) invalid volume set sequence number: " << volsetSeqNo + << " with volume set size: " << volsetSize << endl; + volsetSeqNo = volsetSize; + } + *m_process << "-volset-size" << QString::number(volsetSize); + *m_process << "-volset-seqno" << QString::number(volsetSeqNo); + + if( m_sortWeightFile ) { + *m_process << "-sort" << m_sortWeightFile->name(); + } + + if( m_doc->isoOptions().createRockRidge() ) { + if( m_doc->isoOptions().preserveFilePermissions() ) + *m_process << "-rock"; + else + *m_process << "-rational-rock"; + if( m_rrHideFile ) + *m_process << "-hide-list" << m_rrHideFile->name(); + } + + if( m_doc->isoOptions().createJoliet() ) { + *m_process << "-joliet"; + if( m_doc->isoOptions().jolietLong() ) + *m_process << "-joliet-long"; + if( m_jolietHideFile ) + *m_process << "-hide-joliet-list" << m_jolietHideFile->name(); + } + + if( m_doc->isoOptions().doNotCacheInodes() ) + *m_process << "-no-cache-inodes"; + + // + // Check if we have files > 2 GB and enable udf in that case. + // + bool filesGreaterThan2Gb = false; + K3bDataItem* item = m_doc->root(); + while( (item = item->nextSibling()) ) { + if( item->isFile() && item->size() > 2LL*1024LL*1024LL*1024LL ) { + filesGreaterThan2Gb = true; + break; + } + } + + if( filesGreaterThan2Gb ) { + emit infoMessage( i18n("Found files bigger than 2 GB. These files will only be fully accessible if mounted with UDF."), + WARNING ); + + // in genisoimage 1.1.3 "they" silently introduced this aweful parameter + if ( d->mkisofsBin->hasFeature( "genisoimage" ) && d->mkisofsBin->version >= K3bVersion( 1, 1, 3 ) ) { + *m_process << "-allow-limited-size"; + } + } + + bool udf = m_doc->isoOptions().createUdf(); + if( !udf && filesGreaterThan2Gb ) { + emit infoMessage( i18n("Enabling UDF extension."), INFO ); + udf = true; + } + if( udf ) + *m_process << "-udf"; + + if( m_doc->isoOptions().ISOuntranslatedFilenames() ) { + *m_process << "-untranslated-filenames"; + } + else { + if( m_doc->isoOptions().ISOallowPeriodAtBegin() ) + *m_process << "-allow-leading-dots"; + if( m_doc->isoOptions().ISOallow31charFilenames() ) + *m_process << "-full-iso9660-filenames"; + if( m_doc->isoOptions().ISOomitVersionNumbers() && !m_doc->isoOptions().ISOmaxFilenameLength() ) + *m_process << "-omit-version-number"; + if( m_doc->isoOptions().ISOrelaxedFilenames() ) + *m_process << "-relaxed-filenames"; + if( m_doc->isoOptions().ISOallowLowercase() ) + *m_process << "-allow-lowercase"; + if( m_doc->isoOptions().ISOnoIsoTranslate() ) + *m_process << "-no-iso-translate"; + if( m_doc->isoOptions().ISOallowMultiDot() ) + *m_process << "-allow-multidot"; + if( m_doc->isoOptions().ISOomitTrailingPeriod() ) + *m_process << "-omit-period"; + } + + if( m_doc->isoOptions().ISOmaxFilenameLength() ) + *m_process << "-max-iso9660-filenames"; + + if( m_noDeepDirectoryRelocation ) + *m_process << "-disable-deep-relocation"; + + // We do our own following +// if( m_doc->isoOptions().followSymbolicLinks() || !m_doc->isoOptions().createRockRidge() ) +// *m_process << "-follow-links"; + + if( m_doc->isoOptions().createTRANS_TBL() ) + *m_process << "-translation-table"; + if( m_doc->isoOptions().hideTRANS_TBL() ) + *m_process << "-hide-joliet-trans-tbl"; + + *m_process << "-iso-level" << QString::number(m_doc->isoOptions().ISOLevel()); + + if( m_doc->isoOptions().forceInputCharset() ) + *m_process << "-input-charset" << m_doc->isoOptions().inputCharset(); + + *m_process << "-path-list" << QFile::encodeName(m_pathSpecFile->name()); + + + // boot stuff + if( !m_doc->bootImages().isEmpty() ) { + bool first = true; + for( QPtrListIterator<K3bBootItem> it( m_doc->bootImages() ); + *it; ++it ) { + if( !first ) + *m_process << "-eltorito-alt-boot"; + + K3bBootItem* bootItem = *it; + + *m_process << "-eltorito-boot"; + *m_process << bootItem->writtenPath(); + + if( bootItem->imageType() == K3bBootItem::HARDDISK ) { + *m_process << "-hard-disk-boot"; + } + else if( bootItem->imageType() == K3bBootItem::NONE ) { + *m_process << "-no-emul-boot"; + if( bootItem->loadSegment() > 0 ) + *m_process << "-boot-load-seg" << QString::number(bootItem->loadSegment()); + if( bootItem->loadSize() > 0 ) + *m_process << "-boot-load-size" << QString::number(bootItem->loadSize()); + } + + if( bootItem->imageType() != K3bBootItem::NONE && bootItem->noBoot() ) + *m_process << "-no-boot"; + if( bootItem->bootInfoTable() ) + *m_process << "-boot-info-table"; + + first = false; + } + + *m_process << "-eltorito-catalog" << m_doc->bootCataloge()->writtenPath(); + } + + + // additional parameters from config + const QStringList& params = k3bcore->externalBinManager()->binObject( "mkisofs" )->userParameters(); + for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it ) + *m_process << *it; + + return true; +} + + +int K3bIsoImager::writePathSpec() +{ + delete m_pathSpecFile; + m_pathSpecFile = new KTempFile(); + m_pathSpecFile->setAutoDelete(true); + + if( QTextStream* t = m_pathSpecFile->textStream() ) { + // recursive path spec writing + int num = writePathSpecForDir( m_doc->root(), *t ); + + m_pathSpecFile->close(); + + return num; + } + else + return -1; +} + + +int K3bIsoImager::writePathSpecForDir( K3bDirItem* dirItem, QTextStream& stream ) +{ + if( !m_noDeepDirectoryRelocation && dirItem->depth() > 7 ) { + kdDebug() << "(K3bIsoImager) found directory depth > 7. Enabling no deep directory relocation." << endl; + m_noDeepDirectoryRelocation = true; + } + + // now create the graft points + int num = 0; + for( QPtrListIterator<K3bDataItem> it( dirItem->children() ); it.current(); ++it ) { + K3bDataItem* item = it.current(); + bool writeItem = item->writeToCd(); + + if( item->isSymLink() ) { + if( d->usedLinkHandling == Private::DISCARD_ALL || + ( d->usedLinkHandling == Private::DISCARD_BROKEN && + !item->isValid() ) ) + writeItem = false; + + else if( d->usedLinkHandling == Private::FOLLOW ) { + QFileInfo f( K3b::resolveLink( item->localPath() ) ); + if( !f.exists() ) { + emit infoMessage( i18n("Could not follow link %1 to non-existing file %2. Skipping...") + .arg(item->k3bName()) + .arg(f.filePath()), WARNING ); + writeItem = false; + } + else if( f.isDir() ) { + emit infoMessage( i18n("Ignoring link %1 to folder %2. K3b is unable to follow links to folders.") + .arg(item->k3bName()) + .arg(f.filePath()), WARNING ); + writeItem = false; + } + } + } + else if( item->isFile() ) { + QFileInfo f( item->localPath() ); + if( !f.exists() ) { + emit infoMessage( i18n("Could not find file %1. Skipping...").arg(item->localPath()), WARNING ); + writeItem = false; + } + else if( !f.isReadable() ) { + emit infoMessage( i18n("Could not read file %1. Skipping...").arg(item->localPath()), WARNING ); + writeItem = false; + } + } + + if( writeItem ) { + num++; + + // some versions of mkisofs seem to have a bug that prevents to use filenames + // that contain one or more backslashes + if( item->writtenPath().contains("\\") ) + m_containsFilesWithMultibleBackslashes = true; + + + if( item->isDir() ) { + stream << escapeGraftPoint( item->writtenPath() ) + << "=" + << escapeGraftPoint( dummyDir( static_cast<K3bDirItem*>(item) ) ) << "\n"; + + int x = writePathSpecForDir( dynamic_cast<K3bDirItem*>(item), stream ); + if( x >= 0 ) + num += x; + else + return -1; + } + else { + writePathSpecForFile( static_cast<K3bFileItem*>(item), stream ); + } + } + } + + return num; +} + + +void K3bIsoImager::writePathSpecForFile( K3bFileItem* item, QTextStream& stream ) +{ + stream << escapeGraftPoint( item->writtenPath() ) + << "="; + + if( m_doc->bootImages().containsRef( dynamic_cast<K3bBootItem*>(item) ) ) { // boot-image-backup-hack + + // create temp file + KTempFile temp; + QString tempPath = temp.name(); + temp.unlink(); + + if( !KIO::NetAccess::copy( KURL(item->localPath()), KURL::fromPathOrURL(tempPath) ) ) { + emit infoMessage( i18n("Failed to backup boot image file %1").arg(item->localPath()), ERROR ); + return; + } + + static_cast<K3bBootItem*>(item)->setTempPath( tempPath ); + + m_tempFiles.append(tempPath); + stream << escapeGraftPoint( tempPath ) << "\n"; + } + else if( item->isSymLink() && d->usedLinkHandling == Private::FOLLOW ) + stream << escapeGraftPoint( K3b::resolveLink( item->localPath() ) ) << "\n"; + else + stream << escapeGraftPoint( item->localPath() ) << "\n"; +} + + +bool K3bIsoImager::writeRRHideFile() +{ + delete m_rrHideFile; + m_rrHideFile = new KTempFile(); + m_rrHideFile->setAutoDelete(true); + + if( QTextStream* t = m_rrHideFile->textStream() ) { + + K3bDataItem* item = m_doc->root(); + while( item ) { + if( item->hideOnRockRidge() ) { + if( !item->isDir() ) // hiding directories does not work (all dirs point to the dummy-dir) + *t << escapeGraftPoint( item->localPath() ) << endl; + } + item = item->nextSibling(); + } + + m_rrHideFile->close(); + return true; + } + else + return false; +} + + +bool K3bIsoImager::writeJolietHideFile() +{ + delete m_jolietHideFile; + m_jolietHideFile = new KTempFile(); + m_jolietHideFile->setAutoDelete(true); + + if( QTextStream* t = m_jolietHideFile->textStream() ) { + + K3bDataItem* item = m_doc->root(); + while( item ) { + if( item->hideOnRockRidge() ) { + if( !item->isDir() ) // hiding directories does not work (all dirs point to the dummy-dir but we could introduce a second hidden dummy dir) + *t << escapeGraftPoint( item->localPath() ) << endl; + } + item = item->nextSibling(); + } + + m_jolietHideFile->close(); + return true; + } + else + return false; +} + + +bool K3bIsoImager::writeSortWeightFile() +{ + delete m_sortWeightFile; + m_sortWeightFile = new KTempFile(); + m_sortWeightFile->setAutoDelete(true); + + if( QTextStream* t = m_sortWeightFile->textStream() ) { + // + // We need to write the local path in combination with the sort weight + // mkisofs will take care of multiple entries for one local file and always + // use the highest weight + // + K3bDataItem* item = m_doc->root(); + while( (item = item->nextSibling()) ) { // we skip the root here + if( item->sortWeight() != 0 ) { + if( m_doc->bootImages().containsRef( dynamic_cast<K3bBootItem*>(item) ) ) { // boot-image-backup-hack + *t << escapeGraftPoint( static_cast<K3bBootItem*>(item)->tempPath() ) << " " << item->sortWeight() << endl; + } + else if( item->isDir() ) { + // + // Since we use dummy dirs for all directories in the filesystem and mkisofs uses the local path + // for sorting we need to create a different dummy dir for every sort weight value. + // + *t << escapeGraftPoint( dummyDir( static_cast<K3bDirItem*>(item) ) ) << " " << item->sortWeight() << endl; + } + else + *t << escapeGraftPoint( item->localPath() ) << " " << item->sortWeight() << endl; + } + } + + m_sortWeightFile->close(); + return true; + } + else + return false; +} + + +QString K3bIsoImager::escapeGraftPoint( const QString& str ) +{ + QString enc = str; + + // + // mkisofs manpage (-graft-points) is incorrect (as of mkisofs 2.01.01) + // + // Actually an equal sign needs to be escaped with one backslash only + // Single backslashes inside a filename can be used without change + // while single backslashes at the end of a filename need to be escaped + // with two backslashes. + // + // There is one more problem though: the name in the iso tree can never + // in any number of backslashes. mkisofs simply cannot handle it. So we + // need to remove these slashes somewhere or ignore those files (we do + // that in K3bDataDoc::addUrls) + // + + // + // we do not use QString::replace to have full control + // this might be slow since QString::insert is slow but we don't care + // since this is only called to prepare the iso creation which is not + // time critical. :) + // + + unsigned int pos = 0; + while( pos < enc.length() ) { + // escape every equal sign with one backslash + if( enc[pos] == '=' ) { + enc.insert( pos, "\\" ); + pos += 2; + } + else if( enc[pos] == '\\' ) { + // escape every occurrence of two backslashes with two backslashes + if( pos+1 < enc.length() && enc[pos+1] == '\\' ) { + enc.insert( pos, "\\\\" ); + pos += 4; + } + // escape the last single backslash in the filename (see above) + else if( pos == enc.length()-1 ) { + enc.insert( pos, "\\" ); + pos += 2; + } + else + ++pos; + } + else + ++pos; + } + +// enc.replace( "\\\\", "\\\\\\\\" ); +// enc.replace( "=", "\\=" ); + + return enc; +} + + +bool K3bIsoImager::prepareMkisofsFiles() +{ + // write path spec file + // ---------------------------------------------------- + int num = writePathSpec(); + if( num < 0 ) { + emit infoMessage( i18n("Could not write temporary file"), K3bJob::ERROR ); + return false; + } + else if( num == 0 ) { + emit infoMessage( i18n("No files to be written."), K3bJob::ERROR ); + return false; + } + + if( m_doc->isoOptions().createRockRidge() ) { + if( !writeRRHideFile() ) { + emit infoMessage( i18n("Could not write temporary file"), K3bJob::ERROR ); + return false; + } + } + + if( m_doc->isoOptions().createJoliet() ) { + if( !writeJolietHideFile() ) { + emit infoMessage( i18n("Could not write temporary file"), K3bJob::ERROR ); + return false ; + } + } + + if( !writeSortWeightFile() ) { + emit infoMessage( i18n("Could not write temporary file"), K3bJob::ERROR ); + return false; + } + + return true; +} + + +QString K3bIsoImager::dummyDir( K3bDirItem* dir ) +{ + // + // since we use virtual folders in order to have folders with different weight factors and different + // permissions we create different dummy dirs to be passed to mkisofs + // + + QDir _appDir( locateLocal( "appdata", "temp/" ) ); + + // + // create a unique isoimager session id + // This might become important in case we will allow multiple instances of the isoimager + // to run at the same time. + // + QString jobId = qApp->sessionId() + "_" + QString::number( m_sessionNumber ); + + if( !_appDir.cd( jobId ) ) { + _appDir.mkdir( jobId ); + _appDir.cd( jobId ); + } + + QString name( "dummydir_" ); + name += QString::number( dir->sortWeight() ); + + bool perm = false; + k3b_struct_stat statBuf; + if( !dir->localPath().isEmpty() ) { + // permissions + if( k3b_stat( QFile::encodeName(dir->localPath()), &statBuf ) == 0 ) { + name += "_"; + name += QString::number( statBuf.st_uid ); + name += "_"; + name += QString::number( statBuf.st_gid ); + name += "_"; + name += QString::number( statBuf.st_mode ); + name += "_"; + name += QString::number( statBuf.st_mtime ); + + perm = true; + } + } + + + if( !_appDir.cd( name ) ) { + + kdDebug() << "(K3bIsoImager) creating dummy dir: " << _appDir.absPath() << "/" << name << endl; + + _appDir.mkdir( name ); + _appDir.cd( name ); + + if( perm ) { + ::chmod( QFile::encodeName( _appDir.absPath() ), statBuf.st_mode ); + ::chown( QFile::encodeName( _appDir.absPath() ), statBuf.st_uid, statBuf.st_gid ); + struct utimbuf tb; + tb.actime = tb.modtime = statBuf.st_mtime; + ::utime( QFile::encodeName( _appDir.absPath() ), &tb ); + } + } + + return _appDir.absPath() + "/"; +} + + +void K3bIsoImager::clearDummyDirs() +{ + QString jobId = qApp->sessionId() + "_" + QString::number( m_sessionNumber ); + QDir appDir( locateLocal( "appdata", "temp/" ) ); + if( appDir.cd( jobId ) ) { + QStringList dummyDirEntries = appDir.entryList( "dummydir*", QDir::Dirs ); + for( QStringList::iterator it = dummyDirEntries.begin(); it != dummyDirEntries.end(); ++it ) + appDir.rmdir( *it ); + appDir.cdUp(); + appDir.rmdir( jobId ); + } +} + + +QCString K3bIsoImager::checksum() const +{ + if( K3bChecksumPipe* p = dynamic_cast<K3bChecksumPipe*>( d->pipe ) ) + return p->checksum(); + else + return QCString(); +} + + +bool K3bIsoImager::hasBeenCanceled() const +{ + return m_canceled; +} + +#include "k3bisoimager.moc" diff --git a/libk3b/projects/datacd/k3bisoimager.h b/libk3b/projects/datacd/k3bisoimager.h new file mode 100644 index 0000000..82501ba --- /dev/null +++ b/libk3b/projects/datacd/k3bisoimager.h @@ -0,0 +1,188 @@ +/* + * + * $Id: k3bisoimager.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef K3B_ISO_IMAGER_H +#define K3B_ISO_IMAGER_H + +#include <k3bjob.h> +#include "k3bmkisofshandler.h" + +#include <qptrqueue.h> +#include <qstringlist.h> + +class K3bDataDoc; +class K3bDirItem; +class K3bDataItem; +class K3bFileItem; +class QTextStream; +class K3bProcess; +class KProcess; +class K3bDevice::Device; +class KTempFile; + + +class K3bIsoImager : public K3bJob, public K3bMkisofsHandler +{ + Q_OBJECT + + public: + K3bIsoImager( K3bDataDoc*, K3bJobHandler*, QObject* parent = 0, const char* name = 0 ); + virtual ~K3bIsoImager(); + + virtual bool active() const; + + int size() const { return m_mkisofsPrintSizeResult; } + + virtual bool hasBeenCanceled() const; + + /** + * Get the checksum calculated during the creation of the image. + */ + QCString checksum() const; + + public slots: + /** + * Starts the actual image creation. Always run init() + * before starting the image creation + */ + virtual void start(); + virtual void cancel(); + + /** + * Initialize the image creator. This calculates the image size and performs + * some checks on the project. + * + * The initialization process also finishes with the finished() signal just + * like a normal job operation. Get the calculated image size via size() + */ + virtual void init(); + + /** + * Only calculates the size of the image without the additional checks in + * init() + * + * Use this if you need to recalculate the image size for example if the + * multisession info changed. + */ + virtual void calculateSize(); + + /** + * lets the isoimager write directly into fd instead of writing + * to an image file. + * Be aware that this only makes sense before starting the job. + * To disable just set @p fd to -1 + */ + void writeToFd( int fd ); + + void writeToImageFile( const QString& path ); + + /** + * If dev == 0 K3bIsoImager will ignore the data in the previous session. + * This is usable for CD-Extra. + */ + void setMultiSessionInfo( const QString&, K3bDevice::Device* dev = 0 ); + + K3bDevice::Device* device() const { return m_device; } + K3bDataDoc* doc() const { return m_doc; } + + protected: + virtual void handleMkisofsProgress( int ); + virtual void handleMkisofsInfoMessage( const QString&, int ); + + virtual bool addMkisofsParameters( bool printSize = false ); + + /** + * calls writePathSpec, writeRRHideFile, and writeJolietHideFile + */ + bool prepareMkisofsFiles(); + + /** + * The dummy dir is used to create dirs on the iso-filesystem. + * + * @return an empty dummy dir for use with K3bDirItems. + */ + QString dummyDir( K3bDirItem* ); + + void outputData(); + void initVariables(); + virtual void cleanup(); + void clearDummyDirs(); + + /** + * @returns The number of entries written or -1 on error + */ + virtual int writePathSpec(); + bool writeRRHideFile(); + bool writeJolietHideFile(); + bool writeSortWeightFile(); + + // used by writePathSpec + virtual int writePathSpecForDir( K3bDirItem* dirItem, QTextStream& stream ); + virtual void writePathSpecForFile( K3bFileItem*, QTextStream& stream ); + QString escapeGraftPoint( const QString& str ); + + KTempFile* m_pathSpecFile; + KTempFile* m_rrHideFile; + KTempFile* m_jolietHideFile; + KTempFile* m_sortWeightFile; + + K3bProcess* m_process; + + bool m_processExited; + bool m_canceled; + + protected slots: + virtual void slotReceivedStderr( const QString& ); + virtual void slotProcessExited( KProcess* ); + + private slots: + void slotCollectMkisofsPrintSizeStderr(KProcess*, char*, int); + void slotCollectMkisofsPrintSizeStdout( const QString& ); + void slotMkisofsPrintSizeFinished(); + void slotDataPreparationDone( bool success ); + + private: + void startSizeCalculation(); + + class Private; + Private* d; + + K3bDataDoc* m_doc; + + bool m_noDeepDirectoryRelocation; + + bool m_importSession; + QString m_multiSessionInfo; + K3bDevice::Device* m_device; + + // used for mkisofs -print-size parsing + QString m_collectedMkisofsPrintSizeStdout; + QString m_collectedMkisofsPrintSizeStderr; + int m_mkisofsPrintSizeResult; + + QStringList m_tempFiles; + + int m_fdToWriteTo; + + bool m_containsFilesWithMultibleBackslashes; + + // used to create a unique session id + static int s_imagerSessionCounter; + + int m_sessionNumber; +}; + + +#endif diff --git a/libk3b/projects/datacd/k3bisooptions.cpp b/libk3b/projects/datacd/k3bisooptions.cpp new file mode 100644 index 0000000..bd7314d --- /dev/null +++ b/libk3b/projects/datacd/k3bisooptions.cpp @@ -0,0 +1,216 @@ +/* + * + * $Id: k3bisooptions.cpp 639665 2007-03-05 16:29:52Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bisooptions.h" +#include <k3bcore.h> +#include <k3bversion.h> +#include <k3bglobals.h> + +#include <kconfig.h> +#include <klocale.h> +#include <qstring.h> + + +K3bIsoOptions::K3bIsoOptions() + : m_volumeID( "K3b data project" ), + m_applicationID( QString("K3B THE CD KREATOR (C) 1998-2006 SEBASTIAN TRUEG AND THE K3B TEAM") ), + m_systemId( K3b::systemName().upper() ), + m_inputCharset( "iso8859-1" ), + m_whiteSpaceTreatmentReplaceString( "_" ) +{ + m_bForceInputCharset = false; + + m_createRockRidge = true; + m_createJoliet = true; + m_createUdf = false; + m_ISOallowLowercase = false; + m_ISOallowPeriodAtBegin = false; + m_ISOallow31charFilenames = true; + m_ISOomitVersionNumbers = false; + m_ISOomitTrailingPeriod = false; + m_ISOmaxFilenameLength = false; + m_ISOrelaxedFilenames = false; + m_ISOnoIsoTranslate = false; + m_ISOallowMultiDot = false; + m_ISOuntranslatedFilenames = false; + m_followSymbolicLinks = false; + m_createTRANS_TBL = false; + m_hideTRANS_TBL = false; + m_jolietLong = true; + + m_doNotCacheInodes = true; + + m_isoLevel = 2; + + m_discardSymlinks = false; + m_discardBrokenSymlinks = false; + + m_preserveFilePermissions = false; + + m_whiteSpaceTreatment = noChange; + + m_volumeSetSize = 1; + m_volumeSetNumber = 1; +} + + +void K3bIsoOptions::save( KConfigBase* c, bool saveVolumeDesc ) +{ + if( saveVolumeDesc ) { + c->writeEntry( "volume id", m_volumeID ); + c->writeEntry( "application id", m_applicationID ); + c->writeEntry( "preparer", m_preparer ); + c->writeEntry( "publisher", m_publisher ); + c->writeEntry( "system id", m_systemId ); + c->writeEntry( "volume set id", m_volumeSetId ); + c->writeEntry( "volume set size", m_volumeSetSize ); + c->writeEntry( "volume set number", m_volumeSetNumber ); + c->writeEntry( "abstract file", m_abstractFile ); + c->writeEntry( "copyright file", m_copyrightFile ); + c->writeEntry( "bibliograph file", m_bibliographFile ); + } + + c->writeEntry( "rock_ridge", m_createRockRidge ); + c->writeEntry( "joliet", m_createJoliet ); + c->writeEntry( "udf", m_createUdf ); + + // save iso-level + c->writeEntry( "iso_level", m_isoLevel ); + + c->writeEntry( "create TRANS_TBL", m_createTRANS_TBL ); + c->writeEntry( "hide TRANS_TBL", m_hideTRANS_TBL ); + c->writeEntry( "untranslated filenames", m_ISOuntranslatedFilenames ); + c->writeEntry( "allow 31 character filenames", m_ISOallow31charFilenames ); + c->writeEntry( "max ISO filenames", m_ISOmaxFilenameLength ); + c->writeEntry( "allow beginning period", m_ISOallowPeriodAtBegin ); + c->writeEntry( "relaxed filenames", m_ISOrelaxedFilenames ); + c->writeEntry( "omit version numbers", m_ISOomitVersionNumbers ); + c->writeEntry( "omit trailing period", m_ISOomitTrailingPeriod ); + c->writeEntry( "no iSO translation", m_ISOnoIsoTranslate ); + c->writeEntry( "allow multiple dots", m_ISOallowMultiDot ); + c->writeEntry( "allow lowercase filenames", m_ISOallowLowercase ); + // c->writeEntry( "follow symbolic links", m_followSymbolicLinks ); + + c->writeEntry( "joliet long", m_jolietLong ); + + c->writeEntry( "force input charset", m_bForceInputCharset ); + c->writeEntry( "input charset", m_inputCharset ); + + c->writeEntry( "do not cache inodes", m_doNotCacheInodes ); + + // save whitespace-treatment + switch( m_whiteSpaceTreatment ) { + case strip: + c->writeEntry( "white_space_treatment", "strip" ); + break; + case extended: + c->writeEntry( "white_space_treatment", "extended" ); + break; + case replace: + c->writeEntry( "white_space_treatment", "replace" ); + break; + default: + c->writeEntry( "white_space_treatment", "noChange" ); + } + + c->writeEntry( "whitespace replace string", m_whiteSpaceTreatmentReplaceString ); + + c->writeEntry( "discard symlinks", discardSymlinks() ); + c->writeEntry( "discard broken symlinks", discardBrokenSymlinks() ); + + c->writeEntry( "preserve file permissions", m_preserveFilePermissions ); +} + + +K3bIsoOptions K3bIsoOptions::load( KConfigBase* c, bool loadVolumeDesc ) +{ + K3bIsoOptions options; + + if( loadVolumeDesc ) { + options.setVolumeID( c->readEntry( "volume id", options.volumeID() ) ); + options.setApplicationID( c->readEntry( "application id", options.applicationID() ) ); + options.setPreparer( c->readEntry( "preparer", options.preparer() ) ); + options.setPublisher( c->readEntry( "publisher", options.publisher() ) ); + options.setSystemId( c->readEntry( "system id", options.systemId() ) ); + options.setVolumeSetId( c->readEntry( "volume set id", options.volumeSetId() ) ); + options.setVolumeSetSize( c->readNumEntry( "volume set size", options.volumeSetSize() ) ); + options.setVolumeSetNumber( c->readNumEntry( "volume set number", options.volumeSetNumber() ) ); + options.setAbstractFile( c->readEntry( "abstract file", options.abstractFile() ) ); + options.setCoprightFile( c->readEntry( "copyright file", options.copyrightFile() ) ); + options.setBibliographFile( c->readEntry( "bibliograph file", options.bibliographFile() ) ); + } + + options.setForceInputCharset( c->readBoolEntry( "force input charset", options.forceInputCharset() ) ); + if( options.forceInputCharset() ) + options.setInputCharset( c->readEntry( "input charset", options.inputCharset() ) ); + + options.setCreateRockRidge( c->readBoolEntry( "rock_ridge", options.createRockRidge() ) ); + options.setCreateJoliet( c->readBoolEntry( "joliet", options.createJoliet() ) ); + options.setCreateUdf( c->readBoolEntry( "udf", options.createUdf() ) ); + + options.setISOLevel( c->readNumEntry( "iso_level", options.ISOLevel() ) ); + + options.setCreateTRANS_TBL( c->readBoolEntry( "create TRANS_TBL", options.createTRANS_TBL() ) ); + options.setHideTRANS_TBL( c->readBoolEntry( "hide TRANS_TBL", options.hideTRANS_TBL() ) ); + + // + // We need to use the memeber variables here instead of the access methods + // which do not return the actual value of the member variables but the value + // representing the use in mkisofs (i.e. ISOomitVersionNumbers is also enabled + // if ISOmaxFilenameLength is enabled. + // + options.setISOuntranslatedFilenames( c->readBoolEntry( "untranslated filenames", options.m_ISOuntranslatedFilenames ) ); + options.setISOallow31charFilenames( c->readBoolEntry( "allow 31 character filenames", options.m_ISOallow31charFilenames ) ); + options.setISOmaxFilenameLength( c->readBoolEntry( "max ISO filenames", options.m_ISOmaxFilenameLength ) ); + options.setISOallowPeriodAtBegin( c->readBoolEntry( "allow beginning period", options.m_ISOallowPeriodAtBegin ) ); + options.setISOrelaxedFilenames( c->readBoolEntry( "relaxed filenames", options.m_ISOrelaxedFilenames ) ); + options.setISOomitVersionNumbers( c->readBoolEntry( "omit version numbers", options.m_ISOomitVersionNumbers ) ); + options.setISOnoIsoTranslate( c->readBoolEntry( "no iSO translation", options.m_ISOnoIsoTranslate ) ); + options.setISOallowMultiDot( c->readBoolEntry( "allow multiple dots", options.m_ISOallowMultiDot ) ); + options.setISOallowLowercase( c->readBoolEntry( "allow lowercase filenames", options.m_ISOallowLowercase ) ); + options.setISOomitTrailingPeriod( c->readBoolEntry( "omit trailing period", options.m_ISOomitTrailingPeriod ) ); + + // options.setFollowSymbolicLinks( c->readBoolEntry( "follow symbolic links", options.m_followSymbolicLinks ) ); + + options.setJolietLong( c->readBoolEntry( "joliet long", options.jolietLong() ) ); + + options.setDoNotCacheInodes( c->readBoolEntry( "do not cache inodes", options.doNotCacheInodes() ) ); + + QString w = c->readEntry( "white_space_treatment", "noChange" ); + if( w == "replace" ) + options.setWhiteSpaceTreatment( replace ); + else if( w == "strip" ) + options.setWhiteSpaceTreatment( strip ); + else if( w == "extended" ) + options.setWhiteSpaceTreatment( extended ); + else + options.setWhiteSpaceTreatment( noChange ); + + options.setWhiteSpaceTreatmentReplaceString( c->readEntry( "whitespace replace string", options.whiteSpaceTreatmentReplaceString() ) ); + + options.setDiscardSymlinks( c->readBoolEntry("discard symlinks", options.discardSymlinks() ) ); + options.setDiscardBrokenSymlinks( c->readBoolEntry("discard broken symlinks", options.discardBrokenSymlinks() ) ); + + options.setPreserveFilePermissions( c->readBoolEntry( "preserve file permissions", options.preserveFilePermissions() ) ); + + return options; +} + + +K3bIsoOptions K3bIsoOptions::defaults() +{ + // let the constructor create defaults + return K3bIsoOptions(); +} diff --git a/libk3b/projects/datacd/k3bisooptions.h b/libk3b/projects/datacd/k3bisooptions.h new file mode 100644 index 0000000..254c998 --- /dev/null +++ b/libk3b/projects/datacd/k3bisooptions.h @@ -0,0 +1,183 @@ +/* + * + * $Id: k3bisooptions.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef K3B_ISO_OPTIONS_H +#define K3B_ISO_OPTIONS_H + +#include <qstring.h> +#include "k3b_export.h" + +class KConfigBase; + + +class LIBK3B_EXPORT K3bIsoOptions +{ + public: + K3bIsoOptions(); + + bool forceInputCharset() const { return m_bForceInputCharset; } + const QString& inputCharset() const { return m_inputCharset; } + + void setForceInputCharset( bool b ) { m_bForceInputCharset = b; } + void setInputCharset( const QString& cs ) { m_inputCharset = cs; } + + + // -- mkisofs-options ---------------------------------------------------------------------- + bool createRockRidge() const { return m_createRockRidge; } + bool createJoliet() const { return m_createJoliet; } + bool createUdf() const { return m_createUdf; } + bool ISOallowLowercase() const { return m_ISOallowLowercase || ISOuntranslatedFilenames(); } + bool ISOallowPeriodAtBegin() const { return m_ISOallowPeriodAtBegin || ISOuntranslatedFilenames(); } + bool ISOallow31charFilenames() const { return m_ISOallow31charFilenames || ISOmaxFilenameLength() || ISOuntranslatedFilenames(); } + bool ISOomitVersionNumbers() const { return m_ISOomitVersionNumbers || ISOmaxFilenameLength(); } + bool ISOomitTrailingPeriod() const { return m_ISOomitTrailingPeriod || ISOuntranslatedFilenames(); } + bool ISOmaxFilenameLength() const { return m_ISOmaxFilenameLength || ISOuntranslatedFilenames(); } + bool ISOrelaxedFilenames() const { return m_ISOrelaxedFilenames || ISOuntranslatedFilenames(); } + bool ISOnoIsoTranslate() const { return m_ISOnoIsoTranslate; } + bool ISOallowMultiDot() const { return m_ISOallowMultiDot || ISOuntranslatedFilenames(); } + bool ISOuntranslatedFilenames() const { return m_ISOuntranslatedFilenames; } + bool followSymbolicLinks() const { return m_followSymbolicLinks; } + bool createTRANS_TBL() const { return m_createTRANS_TBL; } + bool hideTRANS_TBL() const { return m_hideTRANS_TBL; } + bool jolietLong() const { return m_jolietLong; } + + bool preserveFilePermissions() const { return m_preserveFilePermissions; } + + int ISOLevel() const { return m_isoLevel; } + const QString& systemId() const { return m_systemId; } + const QString& applicationID() const { return m_applicationID; } + const QString& volumeID() const { return m_volumeID; } + const QString& volumeSetId() const { return m_volumeSetId; } + int volumeSetSize() const { return m_volumeSetSize; } + int volumeSetNumber() const { return m_volumeSetNumber; } + const QString& publisher() const { return m_publisher; } + const QString& preparer() const { return m_preparer; } + const QString& abstractFile() const { return m_abstractFile; } + const QString& copyrightFile() const { return m_copyrightFile; } + const QString& bibliographFile() const { return m_bibliographFile; } + + void setCreateRockRidge( bool b ) { m_createRockRidge = b; } + void setCreateJoliet( bool b ) { m_createJoliet = b; } + void setCreateUdf( bool b ) { m_createUdf = b; } + void setISOallowLowercase( bool b ) { m_ISOallowLowercase = b; } + void setISOallowPeriodAtBegin( bool b ) { m_ISOallowPeriodAtBegin = b; } + void setISOallow31charFilenames( bool b ) { m_ISOallow31charFilenames = b; } + void setISOomitVersionNumbers( bool b ) { m_ISOomitVersionNumbers = b; } + void setISOomitTrailingPeriod( bool b ) { m_ISOomitTrailingPeriod = b; } + void setISOmaxFilenameLength( bool b ) { m_ISOmaxFilenameLength = b; } + void setISOrelaxedFilenames( bool b ) { m_ISOrelaxedFilenames = b; } + void setISOnoIsoTranslate( bool b ) { m_ISOnoIsoTranslate = b; } + void setISOallowMultiDot( bool b ) { m_ISOallowMultiDot = b; } + void setISOuntranslatedFilenames( bool b ) { m_ISOuntranslatedFilenames = b; } + void setFollowSymbolicLinks( bool b ) { m_followSymbolicLinks = b; } + void setCreateTRANS_TBL( bool b ) { m_createTRANS_TBL = b; } + void setHideTRANS_TBL( bool b ) { m_hideTRANS_TBL = b; } + void setJolietLong( bool b ) { m_jolietLong = b; } + + void setISOLevel( int i ) { m_isoLevel = i; } + void setSystemId( const QString& s ) { m_systemId = s; } + void setApplicationID( const QString& s ) { m_applicationID = s; } + + /** + * Set the filesystems volume id. + * + * max length for this field is 32 chars. + */ + void setVolumeID( const QString& s ) { m_volumeID = s; } + void setVolumeSetId( const QString& s ) { m_volumeSetId = s; } + void setVolumeSetSize( int size ) { m_volumeSetSize = size; } + void setVolumeSetNumber( int n ) { m_volumeSetNumber = n; } + void setPublisher( const QString& s ) { m_publisher = s; } + void setPreparer( const QString& s ) { m_preparer = s; } + void setAbstractFile( const QString& s ) { m_abstractFile = s; } + void setCoprightFile( const QString& s ) { m_copyrightFile = s; } + void setBibliographFile( const QString& s ) { m_bibliographFile = s; } + + void setPreserveFilePermissions( bool b ) { m_preserveFilePermissions = b; } + // ----------------------------------------------------------------- mkisofs-options ----------- + + enum whiteSpaceTreatments { noChange = 0, replace = 1, strip = 2, extended = 3 }; + + void setWhiteSpaceTreatment( int i ) { m_whiteSpaceTreatment = i; } + int whiteSpaceTreatment() const { return m_whiteSpaceTreatment; } + const QString& whiteSpaceTreatmentReplaceString() const { return m_whiteSpaceTreatmentReplaceString; } + void setWhiteSpaceTreatmentReplaceString( const QString& s ) { m_whiteSpaceTreatmentReplaceString = s; } + + bool discardSymlinks() const { return m_discardSymlinks; } + void setDiscardSymlinks( bool b ) { m_discardSymlinks = b; } + + bool discardBrokenSymlinks() const { return m_discardBrokenSymlinks; } + void setDiscardBrokenSymlinks( bool b ) { m_discardBrokenSymlinks = b; } + + bool doNotCacheInodes() const { return m_doNotCacheInodes; } + void setDoNotCacheInodes( bool b ) { m_doNotCacheInodes = b; } + + void save( KConfigBase* c, bool saveVolumeDesc = true ); + + static K3bIsoOptions load( KConfigBase* c, bool loadVolumeDesc = true ); + static K3bIsoOptions defaults(); + + private: + // volume descriptor + QString m_volumeID; + QString m_applicationID; + QString m_preparer; + QString m_publisher; + QString m_systemId; + QString m_volumeSetId; + QString m_abstractFile; + QString m_copyrightFile; + QString m_bibliographFile; + + int m_volumeSetSize; + int m_volumeSetNumber; + + bool m_bForceInputCharset; + QString m_inputCharset; + + // mkisofs options ------------------------------------- + bool m_createRockRidge; // -r or -R + bool m_createJoliet; // -J + bool m_createUdf; // -udf + bool m_ISOallowLowercase; // -allow-lowercase + bool m_ISOallowPeriodAtBegin; // -L + bool m_ISOallow31charFilenames; // -I + bool m_ISOomitVersionNumbers; // -N + bool m_ISOomitTrailingPeriod; // -d + bool m_ISOmaxFilenameLength; // -max-iso9660-filenames (forces -N) + bool m_ISOrelaxedFilenames; // -relaxed-filenames + bool m_ISOnoIsoTranslate; // -no-iso-translate + bool m_ISOallowMultiDot; // -allow-multidot + bool m_ISOuntranslatedFilenames; // -U (forces -d, -I, -L, -N, -relaxed-filenames, -allow-lowercase, -allow-multidot, -no-iso-translate) + bool m_followSymbolicLinks; // -f + bool m_createTRANS_TBL; // -T + bool m_hideTRANS_TBL; // -hide-joliet-trans-tbl + + bool m_preserveFilePermissions; // if true -R instead of -r is used + bool m_jolietLong; + + bool m_doNotCacheInodes; + + int m_isoLevel; + + + int m_whiteSpaceTreatment; + QString m_whiteSpaceTreatmentReplaceString; + + bool m_discardSymlinks; + bool m_discardBrokenSymlinks; +}; + +#endif diff --git a/libk3b/projects/datacd/k3bmkisofshandler.cpp b/libk3b/projects/datacd/k3bmkisofshandler.cpp new file mode 100644 index 0000000..a3579ec --- /dev/null +++ b/libk3b/projects/datacd/k3bmkisofshandler.cpp @@ -0,0 +1,150 @@ +/* + * + * $Id: k3bmkisofshandler.cpp 802340 2008-04-29 07:43:07Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bmkisofshandler.h" + +#include <k3bexternalbinmanager.h> +#include <k3bcore.h> +#include <k3bjob.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <cmath> + + + +class K3bMkisofsHandler::Private +{ +public: + const K3bExternalBin* mkisofsBin; + double firstProgressValue; + bool readError; +}; + + +K3bMkisofsHandler::K3bMkisofsHandler() +{ + d = new Private; + d->mkisofsBin = 0; +} + + +K3bMkisofsHandler::~K3bMkisofsHandler() +{ + delete d; +} + + +bool K3bMkisofsHandler::mkisofsReadError() const +{ + return d->readError; +} + + +const K3bExternalBin* K3bMkisofsHandler::initMkisofs() +{ + d->mkisofsBin = k3bcore->externalBinManager()->binObject( "mkisofs" ); + + if( d->mkisofsBin ) { + if( !d->mkisofsBin->copyright.isEmpty() ) + handleMkisofsInfoMessage( i18n("Using %1 %2 - Copyright (C) %3") + .arg("mkisofs").arg(d->mkisofsBin->version).arg(d->mkisofsBin->copyright), + K3bJob::INFO ); + + d->firstProgressValue = -1; + d->readError = false; + } + else { + kdDebug() << "(K3bMkisofsHandler) could not find mkisofs executable" << endl; + handleMkisofsInfoMessage( i18n("Mkisofs executable not found."), K3bJob::ERROR ); + } + + return d->mkisofsBin; +} + + +void K3bMkisofsHandler::parseMkisofsOutput( const QString& line ) +{ + if( !line.isEmpty() ) { + if( line.startsWith( d->mkisofsBin->path ) ) { + // error or warning + QString errorLine = line.mid( d->mkisofsBin->path.length() + 2 ); + if( errorLine.startsWith( "Input/output error. Cannot read from" ) ) { + handleMkisofsInfoMessage( i18n("Read error from file '%1'").arg( errorLine.mid( 38, errorLine.length()-40 ) ), + K3bJob::ERROR ); + d->readError = true; + } + else if( errorLine.startsWith( "Value too large for defined data type" ) ) { + handleMkisofsInfoMessage( i18n("Used version of mkisofs does not have large file support."), K3bJob::ERROR ); + handleMkisofsInfoMessage( i18n("Files bigger than 2 GB cannot be handled."), K3bJob::ERROR ); + d->readError = true; + } + } + else if( line.contains( "done, estimate" ) ) { + int p = parseMkisofsProgress( line ); + if( p != -1 ) + handleMkisofsProgress( p ); + } + else if( line.contains( "extents written" ) ) { + handleMkisofsProgress( 100 ); + } + else if( line.startsWith( "Incorrectly encoded string" ) ) { + handleMkisofsInfoMessage( i18n("Encountered an incorrectly encoded filename '%1'") + .arg(line.section( QRegExp("[\\(\\)]"), 1, 1 )), K3bJob::ERROR ); + handleMkisofsInfoMessage( i18n("This may be caused by a system update which changed the local character set."), K3bJob::ERROR ); + handleMkisofsInfoMessage( i18n("You may use convmv (http://j3e.de/linux/convmv/) to fix the filename encoding."), K3bJob::ERROR ); + d->readError = true; + } + else if( line.endsWith( "has not an allowable size." ) ) { + handleMkisofsInfoMessage( i18n("The boot image has an invalid size."), K3bJob::ERROR ); + d->readError = true; + } + else if( line.endsWith( "has multiple partitions." ) ) { + handleMkisofsInfoMessage( i18n("The boot image contains multiple partitions.."), K3bJob::ERROR ); + handleMkisofsInfoMessage( i18n("A hard-disk boot image has to contain a single partition."), K3bJob::ERROR ); + d->readError = true; + } + else { + kdDebug() << "(mkisofs) " << line << endl; + } + } +} + + +int K3bMkisofsHandler::parseMkisofsProgress( const QString& line ) +{ + // + // in multisession mode mkisofs' progress does not start at 0 but at (X+Y)/X + // where X is the data already on the cd and Y the data to create + // This is not very dramatic but kind or ugly. + // We just save the first emitted progress value and to some math ;) + // + + QString perStr = line; + perStr.truncate( perStr.find('%') ); + bool ok; + double p = perStr.toDouble( &ok ); + if( !ok ) { + kdDebug() << "(K3bMkisofsHandler) Parsing did not work for " << perStr << endl; + return -1; + } + else { + if( d->firstProgressValue < 0 ) + d->firstProgressValue = p; + + return( (int)::ceil( (p - d->firstProgressValue)*100.0/(100.0 - d->firstProgressValue) ) ); + } +} diff --git a/libk3b/projects/datacd/k3bmkisofshandler.h b/libk3b/projects/datacd/k3bmkisofshandler.h new file mode 100644 index 0000000..32576bc --- /dev/null +++ b/libk3b/projects/datacd/k3bmkisofshandler.h @@ -0,0 +1,74 @@ +/* + * + * $Id: k3bmkisofshandler.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_MKISOfS_HANDLER_H_ +#define _K3B_MKISOfS_HANDLER_H_ + +#include <qstring.h> + +class K3bExternalBin; + + +/** + * Derive from this to handle mkisofs. + */ +class K3bMkisofsHandler +{ + public: + K3bMkisofsHandler(); + virtual ~K3bMkisofsHandler(); + + /** + * \return true if there was a read error. + */ + bool mkisofsReadError() const; + + protected: + /** + * Initialize the MkisofsHandler. + * This method emits copyright information and an error message in case mkisofs is not installed + * through handleMkisofsInfoMessage. + * + * \return A mkisofs bin object to be used or 0 if mkisofs is not installed. + */ + const K3bExternalBin* initMkisofs(); + + void parseMkisofsOutput( const QString& line ); + + /** + * Used internally by handleMkisofsOutput. + * May be used in case handleMkisofsOutput is not sufficient. + */ + int parseMkisofsProgress( const QString& line ); + + /** + * Called by handleMkisofsOutput + */ + virtual void handleMkisofsProgress( int ) = 0; + + /** + * Called by handleMkisofsOutput + * + * Uses K3bJob::MessageType + */ + virtual void handleMkisofsInfoMessage( const QString&, int ) = 0; + + private: + class Private; + Private* d; +}; + + +#endif diff --git a/libk3b/projects/datacd/k3bmsinfofetcher.cpp b/libk3b/projects/datacd/k3bmsinfofetcher.cpp new file mode 100644 index 0000000..c30d0ff --- /dev/null +++ b/libk3b/projects/datacd/k3bmsinfofetcher.cpp @@ -0,0 +1,243 @@ +/* + * + * $Id: k3bmsinfofetcher.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bmsinfofetcher.h" + +#include <k3bexternalbinmanager.h> +#include <k3bdevicemanager.h> +#include <k3bdevicehandler.h> +#include <k3bdevice.h> +#include <k3bcore.h> +#include <k3bglobals.h> +#include <k3biso9660.h> + +#include <klocale.h> +#include <kprocess.h> +#include <kdebug.h> + +#include <qstringlist.h> + + +K3bMsInfoFetcher::K3bMsInfoFetcher( K3bJobHandler* jh, QObject* parent, const char* name ) + : K3bJob( jh, parent, name ), + m_process(0), + m_device(0), + m_dvd(false) +{ +} + + +K3bMsInfoFetcher::~K3bMsInfoFetcher() +{ + delete m_process; +} + + +void K3bMsInfoFetcher::start() +{ + jobStarted(); + + emit infoMessage( i18n("Searching previous session"), K3bJob::INFO ); + + if( !k3bcore->externalBinManager()->foundBin( "cdrecord" ) ) { + kdDebug() << "(K3bMsInfoFetcher) could not find cdrecord executable" << endl; + emit infoMessage( i18n("Could not find %1 executable.").arg("cdrecord"), K3bJob::ERROR ); + jobFinished(false); + return; + } + + if( m_device == 0 ) { + kdDebug() << "(K3bMsInfoFetcher) internal error: No device set!" << endl; + jobFinished(false); + return; + } + + // + // first we try to determine if it is a dvd. If so we need to + // read the info on our own + // + + connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::NG_DISKINFO, m_device ), + SIGNAL(finished(K3bDevice::DeviceHandler*)), + this, + SLOT(slotMediaDetectionFinished(K3bDevice::DeviceHandler*)) ); +} + + +void K3bMsInfoFetcher::getMsInfo() +{ + delete m_process; + m_process = new KProcess(); + + const K3bExternalBin* bin = 0; + if( m_dvd ) { + // already handled + } + else { + bin = k3bcore->externalBinManager()->binObject( "cdrecord" ); + + if( !bin ) { + emit infoMessage( i18n("Could not find %1 executable.").arg( m_dvd ? "dvdrecord" : "cdrecord" ), ERROR ); + jobFinished(false); + return; + } + + *m_process << bin->path; + + // add the device (e.g. /dev/sg1) + *m_process << QString("dev=%1").arg( K3b::externalBinDeviceParameter(m_device, bin) ); + + *m_process << "-msinfo"; + + // additional user parameters from config + const QStringList& params = bin->userParameters(); + for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it ) + *m_process << *it; + + kdDebug() << "***** " << bin->name() << " parameters:\n"; + const QValueList<QCString>& args = m_process->args(); + QString s; + for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) { + s += *it + " "; + } + kdDebug() << s << flush << endl; + emit debuggingOutput( "msinfo command:", s ); + + + // connect( m_process, SIGNAL(receivedStderr(KProcess*, char*, int)), + // this, SLOT(slotCollectOutput(KProcess*, char*, int)) ); + connect( m_process, SIGNAL(receivedStdout(KProcess*, char*, int)), + this, SLOT(slotCollectOutput(KProcess*, char*, int)) ); + connect( m_process, SIGNAL(processExited(KProcess*)), + this, SLOT(slotProcessExited()) ); + + m_msInfo = QString::null; + m_collectedOutput = QString::null; + m_canceled = false; + + if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) { + emit infoMessage( i18n("Could not start %1.").arg(bin->name()), K3bJob::ERROR ); + jobFinished(false); + } + } +} + + +void K3bMsInfoFetcher::slotMediaDetectionFinished( K3bDevice::DeviceHandler* h ) +{ + if( h->success() ) { + m_dvd = h->diskInfo().isDvdMedia(); + } + else { + // for now we just default to cd and go on with the detecting + m_dvd = false; + } + + if( m_dvd ) { + if( h->diskInfo().mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) ) { + // get info from iso filesystem + K3bIso9660 iso( m_device, h->toc().last().firstSector().lba() ); + if( iso.open() ) { + unsigned long long nextSession = iso.primaryDescriptor().volumeSpaceSize; + // pad to closest 32K boundary + nextSession += 15; + nextSession /= 16; + nextSession *= 16; + m_msInfo.sprintf( "16,%llu", nextSession ); + + jobFinished( true ); + } + else { + emit infoMessage( i18n("Could not open Iso9660 filesystem in %1.") + .arg( m_device->vendor() + " " + m_device->description() ), ERROR ); + jobFinished( false ); + } + } + else { + unsigned int lastSessionStart, nextWritableAdress; + if( m_device->getNextWritableAdress( lastSessionStart, nextWritableAdress ) ) { + m_msInfo.sprintf( "%u,%u", lastSessionStart+16, nextWritableAdress ); + jobFinished( true ); + } + else { + emit infoMessage( i18n("Could not determine next writable address."), ERROR ); + jobFinished( false ); + } + } + } + else // call cdrecord + getMsInfo(); +} + + +void K3bMsInfoFetcher::slotProcessExited() +{ + if( m_canceled ) + return; + + kdDebug() << "(K3bMsInfoFetcher) msinfo fetched" << endl; + + // now parse the output + QString firstLine = m_collectedOutput.left( m_collectedOutput.find("\n") ); + QStringList list = QStringList::split( ",", firstLine ); + if( list.count() == 2 ) { + bool ok1, ok2; + m_lastSessionStart = list.first().toInt( &ok1 ); + m_nextSessionStart = list[1].toInt( &ok2 ); + if( ok1 && ok2 ) + m_msInfo = firstLine.stripWhiteSpace(); + else + m_msInfo = QString::null; + } + else { + m_msInfo = QString::null; + } + + kdDebug() << "(K3bMsInfoFetcher) msinfo parsed: " << m_msInfo << endl; + + if( m_msInfo.isEmpty() ) { + emit infoMessage( i18n("Could not retrieve multisession information from disk."), K3bJob::ERROR ); + emit infoMessage( i18n("The disk is either empty or not appendable."), K3bJob::ERROR ); + jobFinished(false); + } + else { + jobFinished(true); + } +} + + +void K3bMsInfoFetcher::slotCollectOutput( KProcess*, char* output, int len ) +{ + emit debuggingOutput( "msinfo", QString::fromLocal8Bit( output, len ) ); + + m_collectedOutput += QString::fromLocal8Bit( output, len ); +} + + +void K3bMsInfoFetcher::cancel() +{ + // FIXME: this does not work if the devicehandler is running + + if( m_process ) + if( m_process->isRunning() ) { + m_canceled = true; + m_process->kill(); + emit canceled(); + jobFinished(false); + } +} + + +#include "k3bmsinfofetcher.moc" diff --git a/libk3b/projects/datacd/k3bmsinfofetcher.h b/libk3b/projects/datacd/k3bmsinfofetcher.h new file mode 100644 index 0000000..593664f --- /dev/null +++ b/libk3b/projects/datacd/k3bmsinfofetcher.h @@ -0,0 +1,64 @@ +/* + * + * $Id: k3bmsinfofetcher.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef K3B_MSINFO_FETCHER_H +#define K3B_MSINFO_FETCHER_H + +#include <k3bjob.h> + +namespace K3bDevice { + class Device; + class DeviceHandler; +} +class KProcess; + +class K3bMsInfoFetcher : public K3bJob +{ + Q_OBJECT + + public: + K3bMsInfoFetcher( K3bJobHandler*, QObject* parent = 0, const char* name = 0 ); + ~K3bMsInfoFetcher(); + + const QString& msInfo() const { return m_msInfo; } + int lastSessionStart() const { return m_lastSessionStart; } + int nextSessionStart() const { return m_nextSessionStart; } + + public slots: + void start(); + void cancel(); + + void setDevice( K3bDevice::Device* dev ) { m_device = dev; } + + private slots: + void slotProcessExited(); + void slotCollectOutput( KProcess*, char* output, int len ); + void slotMediaDetectionFinished( K3bDevice::DeviceHandler* ); + void getMsInfo(); + + private: + QString m_msInfo; + int m_lastSessionStart; + int m_nextSessionStart; + QString m_collectedOutput; + + KProcess* m_process; + K3bDevice::Device* m_device; + + bool m_canceled; + bool m_dvd; +}; + +#endif diff --git a/libk3b/projects/datacd/k3bsessionimportitem.cpp b/libk3b/projects/datacd/k3bsessionimportitem.cpp new file mode 100644 index 0000000..35f7936 --- /dev/null +++ b/libk3b/projects/datacd/k3bsessionimportitem.cpp @@ -0,0 +1,59 @@ +/* + * + * $Id: k3bsessionimportitem.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bsessionimportitem.h" +#include "k3bfileitem.h" +#include "k3bdiritem.h" + +#include <k3biso9660.h> + + +K3bSessionImportItem::K3bSessionImportItem( const K3bIso9660File* isoF, K3bDataDoc* doc, K3bDirItem* dir ) + : K3bDataItem( doc, dir ), + m_replaceItem(0), + m_size( isoF->size() ) + +{ + setK3bName( isoF->name() ); + + // add automagically like a qlistviewitem + if( parent() ) + parent()->addDataItem( this ); +} + + +K3bSessionImportItem::K3bSessionImportItem( const K3bSessionImportItem& item ) + : K3bDataItem( item ), + m_replaceItem( item.m_replaceItem ), + m_size( item.m_size ) +{ +} + + +K3bSessionImportItem::~K3bSessionImportItem() +{ + if( m_replaceItem ) + m_replaceItem->setReplacedItemFromOldSession(0); + + // remove this from parentdir + if( parent() ) + parent()->takeDataItem( this ); +} + + +K3bDataItem* K3bSessionImportItem::copy() const +{ + return new K3bSessionImportItem( *this ); +} diff --git a/libk3b/projects/datacd/k3bsessionimportitem.h b/libk3b/projects/datacd/k3bsessionimportitem.h new file mode 100644 index 0000000..33f8124 --- /dev/null +++ b/libk3b/projects/datacd/k3bsessionimportitem.h @@ -0,0 +1,63 @@ +/* + * + * $Id: k3bsessionimportitem.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_SESSION_IMPORT_ITEM_H_ +#define _K3B_SESSION_IMPORT_ITEM_H_ + + +#include "k3bdataitem.h" + + +class K3bDataDoc; +class K3bFileItem; +class K3bDirItem; +class K3bIso9660File; + + +class K3bSessionImportItem : public K3bDataItem +{ + public: + K3bSessionImportItem( const K3bIso9660File*, K3bDataDoc* doc, K3bDirItem* ); + K3bSessionImportItem( const K3bSessionImportItem& ); + ~K3bSessionImportItem(); + + K3bDataItem* copy() const; + + K3bFileItem* replaceItem() const { return m_replaceItem; } + void setReplaceItem( K3bFileItem* item ) { m_replaceItem = item; } + + bool isFile() const { return false; } + bool isFromOldSession() const { return true; } + + bool isRemoveable() const { return false; } + bool isMoveable() const { return false; } + bool isRenameable() const { return false; } + bool isHideable() const { return false; } + bool writeToCd() const { return false; } + + protected: + // the size of an item from an imported session does not depend + // on the value of followSymlinks + /** + * Normally one does not use this method but K3bDataItem::size() + */ + KIO::filesize_t itemSize( bool ) const { return m_size; } + + private: + K3bFileItem* m_replaceItem; + KIO::filesize_t m_size; +}; + +#endif diff --git a/libk3b/projects/datacd/k3bspecialdataitem.h b/libk3b/projects/datacd/k3bspecialdataitem.h new file mode 100644 index 0000000..05005ed --- /dev/null +++ b/libk3b/projects/datacd/k3bspecialdataitem.h @@ -0,0 +1,76 @@ +/* + * + * $Id: k3bspecialdataitem.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef K3BSPECIALDATAITEM_H +#define K3BSPECIALDATAITEM_H + +#include "k3bdataitem.h" +#include "k3bdiritem.h" + +#include <kio/global.h> + +/** + * This can be used to create fake items like the boot catalog + * It's mainly a K3bDataItem where everything has to be set manually + */ +class K3bSpecialDataItem : public K3bDataItem +{ + public: + K3bSpecialDataItem( K3bDataDoc* doc, KIO::filesize_t size, K3bDirItem* parent = 0, const QString& k3bName = QString::null ) + : K3bDataItem( doc, parent ), + m_size( size ) + { + setK3bName( k3bName ); + + // add automagically like a qlistviewitem + if( parent ) + parent->addDataItem( this ); + } + + K3bSpecialDataItem( const K3bSpecialDataItem& item ) + : K3bDataItem( item ), + m_mimeType( item.m_mimeType ), + m_size( item.m_size ) { + } + + ~K3bSpecialDataItem() { + // remove this from parentdir + if( parent() ) + parent()->takeDataItem( this ); + } + + K3bDataItem* copy() const { + return new K3bSpecialDataItem( *this ); + } + + void setMimeType( const QString& s ) { m_mimeType = s; } + const QString& mimeType() const { return m_mimeType; } + + bool isSpecialFile() const { return true; } + + protected: + /** + * Normally one does not use this method but K3bDataItem::size() + */ + KIO::filesize_t itemSize( bool ) const { return m_size; } + + private: + QString m_mimeType; + KIO::filesize_t m_size; +}; + +#endif + |