summaryrefslogtreecommitdiffstats
path: root/libk3b/projects/datacd
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-03 02:15:56 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-03 02:15:56 +0000
commit50b48aec6ddd451a6d1709c0942477b503457663 (patch)
treea9ece53ec06fd0a2819de7a2a6de997193566626 /libk3b/projects/datacd
downloadk3b-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')
-rw-r--r--libk3b/projects/datacd/Makefile.am35
-rw-r--r--libk3b/projects/datacd/k3bbootitem.cpp58
-rw-r--r--libk3b/projects/datacd/k3bbootitem.h66
-rw-r--r--libk3b/projects/datacd/k3bdatadoc.cpp1376
-rw-r--r--libk3b/projects/datacd/k3bdatadoc.h297
-rw-r--r--libk3b/projects/datacd/k3bdataitem.cpp264
-rw-r--r--libk3b/projects/datacd/k3bdataitem.h225
-rw-r--r--libk3b/projects/datacd/k3bdatajob.cpp972
-rw-r--r--libk3b/projects/datacd/k3bdatajob.h111
-rw-r--r--libk3b/projects/datacd/k3bdatapreparationjob.cpp283
-rw-r--r--libk3b/projects/datacd/k3bdatapreparationjob.h51
-rw-r--r--libk3b/projects/datacd/k3bdiritem.cpp406
-rw-r--r--libk3b/projects/datacd/k3bdiritem.h155
-rw-r--r--libk3b/projects/datacd/k3bfilecompilationsizehandler.cpp228
-rw-r--r--libk3b/projects/datacd/k3bfilecompilationsizehandler.h73
-rw-r--r--libk3b/projects/datacd/k3bfileitem.cpp300
-rw-r--r--libk3b/projects/datacd/k3bfileitem.h124
-rw-r--r--libk3b/projects/datacd/k3bisoimager.cpp1187
-rw-r--r--libk3b/projects/datacd/k3bisoimager.h188
-rw-r--r--libk3b/projects/datacd/k3bisooptions.cpp216
-rw-r--r--libk3b/projects/datacd/k3bisooptions.h183
-rw-r--r--libk3b/projects/datacd/k3bmkisofshandler.cpp150
-rw-r--r--libk3b/projects/datacd/k3bmkisofshandler.h74
-rw-r--r--libk3b/projects/datacd/k3bmsinfofetcher.cpp243
-rw-r--r--libk3b/projects/datacd/k3bmsinfofetcher.h64
-rw-r--r--libk3b/projects/datacd/k3bsessionimportitem.cpp59
-rw-r--r--libk3b/projects/datacd/k3bsessionimportitem.h63
-rw-r--r--libk3b/projects/datacd/k3bspecialdataitem.h76
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
+