2018-09-16 05:28:46 +02:00
# include <QCoreApplication>
# include <QDebug>
# include <QDir>
# include <QFile>
# include <qmath.h>
# include <QDataStream>
# include <QDateTime>
# include <QCryptographicHash>
# include <QJsonObject>
# include <QJsonArray>
# include <QJsonDocument>
# include <QCommandLineParser>
# include <QCommandLineOption>
# include <QStringBuilder>
# include <QUuid>
2018-09-18 22:07:27 +02:00
# include <QtGlobal>
2018-09-16 05:28:46 +02:00
2018-10-14 12:11:41 +02:00
# include "utils/fileutils.h"
2018-09-16 05:28:46 +02:00
bool writeBitmap ( const QString & filename , const QByteArray & content )
{
qDebug ( ) < < " writeBitmap " < < filename ;
QFile file ( filename ) ;
if ( ! file . open ( QIODevice : : WriteOnly | QIODevice : : Truncate ) )
{
qWarning ( ) < < " could not open file " < < file . errorString ( ) ;
return false ;
}
2019-04-19 21:15:26 +02:00
const quint64 pixels = std : : ceil ( content . length ( ) / 4.0 ) ;
const quint32 width = std : : sqrt ( pixels ) ;
const quint32 height = std : : ceil ( pixels / ( qreal ) width ) ;
2018-09-16 05:28:46 +02:00
2018-10-05 11:50:56 +02:00
const quint32 bitmapSize = width * height * 4 ;
2018-09-16 05:28:46 +02:00
{
QDataStream dataStream ( & file ) ;
dataStream . setByteOrder ( QDataStream : : LittleEndian ) ;
//BMP Header
dataStream < < ( quint16 ) 0x4D42 ; //BM-Header
dataStream < < ( quint32 ) ( 54 + bitmapSize ) ; //File size
dataStream < < content . length ( ) ; //Unused (and abused for content length)
dataStream < < ( quint32 ) 54 ; //Offset to bitmap data
//DIB Header
dataStream < < ( quint32 ) 40 ; //DIP Header size
dataStream < < width ; //width
dataStream < < height ; //height
dataStream < < ( quint16 ) 1 ; //Number of color planes
dataStream < < ( quint16 ) 32 ; //Bits per pixel
dataStream < < ( quint32 ) 0 ; //No compression;
dataStream < < bitmapSize ; //Size of bitmap data
dataStream < < ( quint32 ) 2835 ; //Horizontal print resolution
dataStream < < ( quint32 ) 2835 ; //Horizontal print resolution
dataStream < < ( quint32 ) 0 ; //Number of colors in palette
dataStream < < ( quint32 ) 0 ; //Important colors
}
file . write ( content ) ;
2018-10-05 11:50:56 +02:00
file . write ( QByteArray ( ( width * height * 4 ) - content . length ( ) , ' \0 ' ) ) ;
2018-09-16 05:28:46 +02:00
return true ;
}
bool readBitmap ( const QString & filename , QByteArray & content )
{
qDebug ( ) < < " readBitmap " < < filename ;
QFile file ( filename ) ;
if ( ! file . exists ( ) )
{
qWarning ( ) < < " file does not exist " ;
return false ;
}
if ( ! file . open ( QIODevice : : ReadOnly ) )
{
qWarning ( ) < < " could not open file " < < file . errorString ( ) ;
return false ;
}
if ( file . size ( ) < 14 )
{
qWarning ( ) < < " not enough bytes " ;
return false ;
}
QDataStream dataStream ( & file ) ;
dataStream . setByteOrder ( QDataStream : : LittleEndian ) ;
{
quint16 bmHeader ;
dataStream > > bmHeader ;
if ( bmHeader ! = 0x4D42 )
{
qWarning ( ) < < " no BM header " ;
return false ;
}
}
{
quint32 filesize ;
dataStream > > filesize ;
if ( filesize ! = file . size ( ) )
{
qWarning ( ) < < " file size does not match! " ;
return false ;
}
}
quint32 usedSize ;
dataStream > > usedSize ;
quint32 offsetBitmapData ;
dataStream > > offsetBitmapData ;
if ( ! file . seek ( offsetBitmapData ) )
{
qWarning ( ) < < " could not seek " ;
return false ;
}
content = file . read ( usedSize ) ;
if ( content . length ( ) ! = usedSize )
{
qWarning ( ) < < " could not read enough for usedSize " ;
return false ;
}
return true ;
}
bool spread ( const QString & sourcePath , const QString & targetPath )
{
qDebug ( ) < < " spread " < < sourcePath < < targetPath ;
2018-10-05 11:50:56 +02:00
const QDir targetDir ( targetPath ) ;
2018-09-16 05:28:46 +02:00
if ( ! targetDir . mkpath ( targetDir . absolutePath ( ) ) )
{
qWarning ( ) < < " could not create target dir " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const QFileInfo sourceFileInfo ( sourcePath ) ;
2018-09-16 05:28:46 +02:00
if ( ! sourceFileInfo . exists ( ) )
{
qWarning ( ) < < " source does not exist " ;
return false ;
}
if ( sourceFileInfo . isFile ( ) )
{
bool rewriteIndex = false ;
2018-10-05 11:50:56 +02:00
if ( QFile : : exists ( targetDir . absoluteFilePath ( QStringLiteral ( " __index.bmp " ) ) ) )
2018-09-16 05:28:46 +02:00
{
QByteArray content ;
2018-10-05 11:50:56 +02:00
if ( readBitmap ( targetDir . absoluteFilePath ( QStringLiteral ( " __index.bmp " ) ) , content ) )
2018-09-16 05:28:46 +02:00
{
QJsonParseError error ;
auto document = QJsonDocument : : fromJson ( content , & error ) ;
if ( error . error ! = QJsonParseError : : NoError )
{
qWarning ( ) < < " index is invalid: error parsing json " < < error . errorString ( ) ;
return false ;
}
if ( ! document . isObject ( ) )
{
qWarning ( ) < < " index is invalid: json is not an object " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto jsonObject = document . object ( ) ;
2018-09-16 05:28:46 +02:00
2018-10-05 11:50:56 +02:00
if ( ! jsonObject . contains ( QStringLiteral ( " type " ) ) )
2018-09-16 05:28:46 +02:00
{
qWarning ( ) < < " index is invalid: json does not contain type " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto typeValue = jsonObject . value ( QStringLiteral ( " type " ) ) ;
2018-09-16 05:28:46 +02:00
if ( typeValue . type ( ) ! = QJsonValue : : String )
{
qWarning ( ) < < " index is invalid: json type is not a string " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto type = typeValue . toString ( ) ;
2018-09-16 05:28:46 +02:00
2018-10-05 11:50:56 +02:00
if ( type = = QStringLiteral ( " file " ) )
2018-09-16 05:28:46 +02:00
{
//TODO: compare lastmodified and size
}
2018-10-05 11:50:56 +02:00
else if ( type = = QStringLiteral ( " directory " ) )
2018-09-16 05:28:46 +02:00
{
qInfo ( ) < < " type changed from file to directory " ;
if ( ! emptyDirectory ( targetDir . absolutePath ( ) ) )
return false ;
rewriteIndex = true ;
}
else
{
qWarning ( ) < < " index is invalid: unknown type " < < type ;
rewriteIndex = true ;
}
}
else
rewriteIndex = true ;
}
else
rewriteIndex = true ;
if ( rewriteIndex )
{
if ( ! emptyDirectory ( targetPath ) )
return false ;
QFile sourceFile ( sourcePath ) ;
if ( ! sourceFile . open ( QIODevice : : ReadOnly ) )
{
qWarning ( ) < < " could not open source file " < < sourceFile . errorString ( ) ;
return false ;
}
QJsonArray parts ;
QCryptographicHash hash ( QCryptographicHash : : Sha512 ) ;
while ( sourceFile . pos ( ) < sourceFile . size ( ) )
{
QString filename ;
QString completePath ;
do
{
2018-10-05 11:50:56 +02:00
filename = QUuid : : createUuid ( ) . toString ( ) . remove ( QLatin1Char ( ' { ' ) ) . remove ( QLatin1Char ( ' } ' ) ) % " .bmp " ;
2018-09-16 05:28:46 +02:00
completePath = targetDir . absoluteFilePath ( filename ) ;
}
while ( QFileInfo ( completePath ) . exists ( ) ) ;
QJsonObject part ;
2018-10-05 11:50:56 +02:00
part [ QStringLiteral ( " filename " ) ] = filename ;
part [ QStringLiteral ( " startPos " ) ] = sourceFile . pos ( ) ;
2018-09-16 05:28:46 +02:00
2018-10-05 11:50:56 +02:00
const auto buffer = sourceFile . read ( 2048 * 2048 * 4 ) ;
2018-09-16 05:28:46 +02:00
hash . addData ( buffer ) ;
2018-10-05 11:50:56 +02:00
part [ QStringLiteral ( " endPos " ) ] = sourceFile . pos ( ) ;
part [ QStringLiteral ( " length " ) ] = buffer . length ( ) ;
2018-09-16 05:28:46 +02:00
if ( ! writeBitmap ( completePath , buffer ) )
return false ;
parts . append ( part ) ;
}
QJsonObject jsonObject ;
2018-10-05 11:50:56 +02:00
jsonObject [ QStringLiteral ( " type " ) ] = QStringLiteral ( " file " ) ;
jsonObject [ QStringLiteral ( " filesize " ) ] = sourceFileInfo . size ( ) ;
2018-09-18 22:07:27 +02:00
2018-10-05 11:50:56 +02:00
jsonObject [ QStringLiteral ( " birthTime " ) ] = sourceFileInfo
2018-09-22 07:28:48 +02:00
# if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
//deprecated since 5.10
2018-09-18 22:07:27 +02:00
. created ( )
2018-09-22 07:28:48 +02:00
# else
. birthTime ( )
2018-09-18 22:07:27 +02:00
# endif
. toMSecsSinceEpoch ( ) ;
2018-10-05 11:50:56 +02:00
jsonObject [ QStringLiteral ( " lastModified " ) ] = sourceFileInfo . lastModified ( ) . toMSecsSinceEpoch ( ) ;
jsonObject [ QStringLiteral ( " lastRead " ) ] = sourceFileInfo . lastRead ( ) . toMSecsSinceEpoch ( ) ;
jsonObject [ QStringLiteral ( " sha512 " ) ] = QString ( hash . result ( ) . toHex ( ) ) ;
jsonObject [ QStringLiteral ( " parts " ) ] = parts ;
if ( ! writeBitmap ( targetDir . absoluteFilePath ( QStringLiteral ( " __index.bmp " ) ) ,
QJsonDocument ( jsonObject ) . toJson ( /* QJsonDocument::Compact */ ) ) ) //amazon has enough storage for spaces!
2018-09-16 05:28:46 +02:00
return false ;
}
}
else if ( sourceFileInfo . isDir ( ) )
{
2018-10-05 11:50:56 +02:00
const QDir sourceDir ( sourcePath ) ;
2018-09-16 05:28:46 +02:00
if ( ! sourceDir . exists ( ) )
{
qWarning ( ) < < " source dir does not exist " ;
return false ;
}
bool rewriteIndex = false ;
QStringList oldEntries ;
2018-10-05 11:50:56 +02:00
if ( QFile : : exists ( targetDir . absoluteFilePath ( QStringLiteral ( " __index.bmp " ) ) ) )
2018-09-16 05:28:46 +02:00
{
QByteArray content ;
2018-10-05 11:50:56 +02:00
if ( readBitmap ( targetDir . absoluteFilePath ( QStringLiteral ( " __index.bmp " ) ) , content ) )
2018-09-16 05:28:46 +02:00
{
QJsonParseError error ;
2018-10-05 11:50:56 +02:00
const auto document = QJsonDocument : : fromJson ( content , & error ) ;
2018-09-16 05:28:46 +02:00
if ( error . error ! = QJsonParseError : : NoError )
{
qWarning ( ) < < " index is invalid: error parsing json " < < error . errorString ( ) ;
return false ;
}
if ( ! document . isObject ( ) )
{
qWarning ( ) < < " index is invalid: json is not an object " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto jsonObject = document . object ( ) ;
2018-09-16 05:28:46 +02:00
2018-10-05 11:50:56 +02:00
if ( ! jsonObject . contains ( QStringLiteral ( " type " ) ) )
2018-09-16 05:28:46 +02:00
{
qWarning ( ) < < " index is invalid: json does not contain type " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto typeValue = jsonObject . value ( QStringLiteral ( " type " ) ) ;
2018-09-16 05:28:46 +02:00
if ( typeValue . type ( ) ! = QJsonValue : : String )
{
qWarning ( ) < < " index is invalid: json type is not a string " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto type = typeValue . toString ( ) ;
2018-09-16 05:28:46 +02:00
2018-10-05 11:50:56 +02:00
if ( type = = QStringLiteral ( " file " ) )
2018-09-16 05:28:46 +02:00
{
qInfo ( ) < < " type changed from directory to file " ;
if ( ! emptyDirectory ( targetDir . absolutePath ( ) ) )
return false ;
rewriteIndex = true ;
}
2018-10-05 11:50:56 +02:00
else if ( type = = QStringLiteral ( " directory " ) )
2018-09-16 05:28:46 +02:00
{
if ( ! jsonObject . contains ( " entries " ) )
{
qWarning ( ) < < " index is invalid: json does not contain entries " ;
rewriteIndex = true ;
}
2018-10-05 11:50:56 +02:00
const auto entriesValue = jsonObject . value ( QStringLiteral ( " entries " ) ) ;
2018-09-16 05:28:46 +02:00
if ( entriesValue . type ( ) ! = QJsonValue : : Array )
{
qWarning ( ) < < " index is invalid: json entries is not an array " ;
rewriteIndex = true ;
}
2018-10-05 11:50:56 +02:00
const auto entries = entriesValue . toArray ( ) ;
2018-09-16 05:28:46 +02:00
2018-10-05 11:50:56 +02:00
for ( auto value : entries )
2018-09-16 05:28:46 +02:00
{
if ( value . type ( ) ! = QJsonValue : : String )
{
qWarning ( ) < < " index is invalid: json entry is not a string " ;
rewriteIndex = true ;
break ;
}
oldEntries . append ( value . toString ( ) ) ;
}
}
else
{
qWarning ( ) < < " index is invalid: unknown type " < < type ;
rewriteIndex = true ;
}
}
else
rewriteIndex = true ;
}
else
rewriteIndex = true ;
for ( const auto & oldEntry : oldEntries )
{
2018-10-05 11:50:56 +02:00
const QFileInfo oldFileInfo ( sourceDir . absoluteFilePath ( oldEntry ) ) ;
2018-09-16 05:28:46 +02:00
if ( ! oldFileInfo . exists ( ) )
{
qInfo ( ) < < " deleted " < < sourceDir . absoluteFilePath ( oldEntry ) ;
if ( ! QDir ( targetDir . absoluteFilePath ( oldEntry ) ) . removeRecursively ( ) )
{
qWarning ( ) < < " could not remove dir " < < targetDir . absoluteFilePath ( oldEntry ) ;
return false ;
}
rewriteIndex = true ;
}
}
QJsonArray entries ;
2018-10-05 11:50:56 +02:00
for ( const auto & fileInfo : sourceDir . entryInfoList ( QDir : : Files | QDir : : Dirs | QDir : : NoDotAndDotDot ) )
2018-09-16 05:28:46 +02:00
{
if ( ! oldEntries . contains ( fileInfo . fileName ( ) ) )
{
qInfo ( ) < < " added " < < fileInfo . absoluteFilePath ( ) ;
rewriteIndex = true ;
}
entries . append ( fileInfo . fileName ( ) ) ;
if ( ! spread ( fileInfo . absoluteFilePath ( ) , targetDir . absoluteFilePath ( fileInfo . fileName ( ) ) ) )
return false ;
}
if ( rewriteIndex )
{
QJsonObject jsonObject ;
2018-10-05 11:50:56 +02:00
jsonObject [ QStringLiteral ( " type " ) ] = QStringLiteral ( " directory " ) ;
jsonObject [ QStringLiteral ( " entries " ) ] = entries ;
if ( ! writeBitmap ( targetDir . absoluteFilePath ( QStringLiteral ( " __index.bmp " ) ) ,
QJsonDocument ( jsonObject ) . toJson ( /* QJsonDocument::Compact */ ) ) ) //amazon has enough storage for spaces!
2018-09-16 05:28:46 +02:00
return false ;
}
}
return true ;
}
bool compile ( const QString & sourcePath , const QString & targetPath )
{
qDebug ( ) < < " compile " < < sourcePath < < targetPath ;
QFileInfo sourceFileInfo ( sourcePath ) ;
if ( ! sourceFileInfo . exists ( ) )
{
qWarning ( ) < < " source does not exist " ;
return false ;
}
if ( ! sourceFileInfo . isDir ( ) )
{
qWarning ( ) < < " source is not a dir " ;
return false ;
}
QDir sourceDir ( sourcePath ) ;
QJsonObject jsonObject ;
{
QByteArray content ;
2018-10-05 11:50:56 +02:00
if ( ! readBitmap ( sourceDir . absoluteFilePath ( QStringLiteral ( " __index.bmp " ) ) , content ) )
2018-09-16 05:28:46 +02:00
return false ;
QJsonParseError error ;
auto document = QJsonDocument : : fromJson ( content , & error ) ;
if ( error . error ! = QJsonParseError : : NoError )
{
qWarning ( ) < < " error parsing json " < < error . errorString ( ) ;
return false ;
}
if ( ! document . isObject ( ) )
{
qWarning ( ) < < " json is not an object " ;
return false ;
}
jsonObject = document . object ( ) ;
}
2018-10-05 11:50:56 +02:00
if ( ! jsonObject . contains ( QStringLiteral ( " type " ) ) )
2018-09-16 05:28:46 +02:00
{
qWarning ( ) < < " json does not contain type " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto typeValue = jsonObject . value ( QStringLiteral ( " type " ) ) ;
2018-09-16 05:28:46 +02:00
if ( typeValue . type ( ) ! = QJsonValue : : String )
{
qWarning ( ) < < " json type is not a string " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto type = typeValue . toString ( ) ;
2018-09-16 05:28:46 +02:00
2018-10-05 11:50:56 +02:00
if ( type = = QStringLiteral ( " file " ) )
2018-09-16 05:28:46 +02:00
{
QFile targetFile ( targetPath ) ;
if ( ! targetFile . open ( QIODevice : : WriteOnly | QIODevice : : Truncate ) )
{
qWarning ( ) < < " could not open file " < < targetFile . errorString ( ) ;
return false ;
}
//TODO
}
2018-10-05 11:50:56 +02:00
else if ( type = = QStringLiteral ( " directory " ) )
2018-09-16 05:28:46 +02:00
{
QDir targetDir ( targetPath ) ;
if ( ! targetDir . mkpath ( targetDir . absolutePath ( ) ) )
{
qWarning ( ) < < " could not create dir " ;
return false ;
}
2018-10-05 11:50:56 +02:00
if ( ! jsonObject . contains ( QStringLiteral ( " entries " ) ) )
2018-09-16 05:28:46 +02:00
{
qWarning ( ) < < " json does not contain entries " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto entriesValue = jsonObject . value ( QStringLiteral ( " entries " ) ) ;
2018-09-16 05:28:46 +02:00
if ( entriesValue . type ( ) ! = QJsonValue : : Array )
{
qWarning ( ) < < " json entries is not an array " ;
return false ;
}
2018-10-05 11:50:56 +02:00
const auto entries = entriesValue . toArray ( ) ;
2018-09-16 05:28:46 +02:00
2018-10-05 11:50:56 +02:00
for ( auto entryValue : entries )
2018-09-16 05:28:46 +02:00
{
if ( entryValue . type ( ) ! = QJsonValue : : String )
{
qWarning ( ) < < " json entry is not a string " ;
return false ;
}
auto entry = entryValue . toString ( ) ;
if ( ! compile ( sourceDir . absoluteFilePath ( entry ) , targetDir . absoluteFilePath ( entry ) ) )
return false ;
}
}
else
{
qWarning ( ) < < " unknown type " < < type ;
return false ;
}
return true ;
}
int main ( int argc , char * argv [ ] )
{
QCoreApplication app ( argc , argv ) ;
QCoreApplication : : setApplicationName ( " picsync " ) ;
QCoreApplication : : setApplicationVersion ( " 1.0 " ) ;
qSetMessagePattern ( " %{time dd.MM.yyyy HH:mm:ss.zzz} "
" [ "
" %{if-debug}DEBUG%{endif} "
" %{if-info}INFO%{endif} "
" %{if-warning}WARN%{endif} "
" %{if-critical}CRIT%{endif} "
" %{if-fatal}FATAL%{endif} "
" ] "
" %{function}(): "
" %{message} " ) ;
QCommandLineParser parser ;
parser . setApplicationDescription ( QCoreApplication : : translate ( " main " , " Lets you convert any file into pictures. Mostly used in combination with cloud storage. " ) ) ;
parser . addHelpOption ( ) ;
parser . addVersionOption ( ) ;
QCommandLineOption actionOption ( QStringList ( ) < < " a " < < " action " , QCoreApplication : : translate ( " main " , " Action (spread or compile) " ) , QCoreApplication : : translate ( " main " , " action " ) ) ;
parser . addOption ( actionOption ) ;
QCommandLineOption sourceOption ( QStringList ( ) < < " s " < < " source " , QCoreApplication : : translate ( " main " , " Source file or directory " ) , QCoreApplication : : translate ( " main " , " some_file " ) ) ;
parser . addOption ( sourceOption ) ;
QCommandLineOption targetOption ( QStringList ( ) < < " t " < < " target " , QCoreApplication : : translate ( " main " , " Target directory " ) , QCoreApplication : : translate ( " main " , " some_directory " ) ) ;
parser . addOption ( targetOption ) ;
parser . process ( app ) ;
if ( ! parser . isSet ( actionOption ) )
{
qCritical ( ) < < " no action set " ;
parser . showHelp ( ) ;
return - 1 ;
}
enum { ActionSpread , ActionCompile } action ;
2018-10-05 11:50:56 +02:00
if ( parser . value ( actionOption ) = = QStringLiteral ( " spread " ) )
2018-09-16 05:28:46 +02:00
action = ActionSpread ;
2018-10-05 11:50:56 +02:00
else if ( parser . value ( actionOption ) = = QStringLiteral ( " compile " ) )
2018-09-16 05:28:46 +02:00
action = ActionCompile ;
else
{
qCritical ( ) < < " unknown action " < < parser . value ( actionOption ) ;
parser . showHelp ( ) ;
return - 2 ;
}
if ( ! parser . isSet ( sourceOption ) )
{
qCritical ( ) < < " source not set " ;
parser . showHelp ( ) ;
return - 3 ;
}
QFileInfo sourceFileInfo ( parser . value ( sourceOption ) ) ;
if ( ! sourceFileInfo . exists ( ) )
{
qCritical ( ) < < " source " < < parser . value ( sourceOption ) < < " does not exist " ;
parser . showHelp ( ) ;
return - 4 ;
}
if ( ! sourceFileInfo . isFile ( ) & & ! sourceFileInfo . isDir ( ) )
{
qCritical ( ) < < " source " < < parser . value ( sourceOption ) < < " isnt file nor dir " ;
parser . showHelp ( ) ;
return - 5 ;
}
if ( ! parser . isSet ( targetOption ) )
{
qCritical ( ) < < " target not set " ;
parser . showHelp ( ) ;
return - 6 ;
}
2018-10-05 11:50:56 +02:00
const QFileInfo targetFileInfo ( parser . value ( targetOption ) ) ;
2018-09-16 05:28:46 +02:00
if ( targetFileInfo . exists ( ) & & ! targetFileInfo . isDir ( ) )
{
qCritical ( ) < < " target " < < parser . value ( targetOption ) < < " exists and is not a dir " ;
parser . showHelp ( ) ;
return - 7 ;
}
switch ( action )
{
case ActionSpread :
if ( spread ( sourceFileInfo . absoluteFilePath ( ) , targetFileInfo . absoluteFilePath ( ) ) )
return 0 ;
else
return - 8 ;
case ActionCompile :
if ( compile ( sourceFileInfo . absoluteFilePath ( ) , targetFileInfo . absoluteFilePath ( ) ) )
return 0 ;
else
return - 8 ;
}
Q_UNREACHABLE ( ) ;
}