From 56f5ea2e5054c4feb07a42a1973d64fc210a6c27 Mon Sep 17 00:00:00 2001 From: barracuda156 Date: Tue, 29 Aug 2023 10:25:42 +0800 Subject: [PATCH] Revert "Remove Qt4 support and other changes" This reverts commit be776bcad727ec55c543773f7b895eaa51905fb3. --- archiver/archiver.cpp | 407 +-- debian/changelog | 7 - lib/Global.hpp | 20 +- lib/LibArchive.cpp | 3111 ++++++++++-------- lib/LibBZip2.cpp | 157 +- lib/LibBZip2.hpp | 27 +- lib/LibGZip.cpp | 158 +- lib/LibGZip.hpp | 25 +- lib/LibLzma.cpp | 214 +- lib/LibLzma.hpp | 25 +- lib/LibLzma2.cpp | 226 +- lib/LibLzma2.hpp | 25 +- lib/MimeHandler/NBMimeDatabase.cpp | 704 ++++ lib/MimeHandler/NBMimeDatabase.hpp | 91 + lib/MimeHandler/NBMimeDatabase_p.hpp | 87 + lib/MimeHandler/NBMimeGlobPattern.cpp | 290 ++ lib/MimeHandler/NBMimeGlobPattern_p.hpp | 145 + lib/MimeHandler/NBMimeMagicRule.cpp | 479 +++ lib/MimeHandler/NBMimeMagicRuleMatcher.cpp | 114 + lib/MimeHandler/NBMimeMagicRuleMatcher_p.hpp | 76 + lib/MimeHandler/NBMimeMagicRule_p.hpp | 87 + lib/MimeHandler/NBMimeProvider.cpp | 1059 ++++++ lib/MimeHandler/NBMimeProvider_p.hpp | 163 + lib/MimeHandler/NBMimeType.cpp | 537 +++ lib/MimeHandler/NBMimeType.hpp | 111 + lib/MimeHandler/NBMimeTypeParser.cpp | 433 +++ lib/MimeHandler/NBMimeTypeParser_p.hpp | 116 + lib/MimeHandler/NBMimeType_p.hpp | 111 + lib/StandardPaths/NBStandardPaths.cpp | 306 ++ lib/StandardPaths/NBStandardPaths.hpp | 102 + lib/StandardPaths/NBStandardPaths_unix.cpp | 322 ++ lib/libarchiveqt.h | 221 +- lib/lz4dec.c | 552 ++-- lib/lz4dec.h | 10 +- lib/meson.build | 52 +- meson.build | 37 +- meson_options.txt | 2 +- 37 files changed, 8153 insertions(+), 2456 deletions(-) create mode 100644 lib/MimeHandler/NBMimeDatabase.cpp create mode 100644 lib/MimeHandler/NBMimeDatabase.hpp create mode 100644 lib/MimeHandler/NBMimeDatabase_p.hpp create mode 100644 lib/MimeHandler/NBMimeGlobPattern.cpp create mode 100644 lib/MimeHandler/NBMimeGlobPattern_p.hpp create mode 100644 lib/MimeHandler/NBMimeMagicRule.cpp create mode 100644 lib/MimeHandler/NBMimeMagicRuleMatcher.cpp create mode 100644 lib/MimeHandler/NBMimeMagicRuleMatcher_p.hpp create mode 100644 lib/MimeHandler/NBMimeMagicRule_p.hpp create mode 100644 lib/MimeHandler/NBMimeProvider.cpp create mode 100644 lib/MimeHandler/NBMimeProvider_p.hpp create mode 100644 lib/MimeHandler/NBMimeType.cpp create mode 100644 lib/MimeHandler/NBMimeType.hpp create mode 100644 lib/MimeHandler/NBMimeTypeParser.cpp create mode 100644 lib/MimeHandler/NBMimeTypeParser_p.hpp create mode 100644 lib/MimeHandler/NBMimeType_p.hpp create mode 100644 lib/StandardPaths/NBStandardPaths.cpp create mode 100644 lib/StandardPaths/NBStandardPaths.hpp create mode 100644 lib/StandardPaths/NBStandardPaths_unix.cpp diff --git archiver/archiver.cpp archiver/archiver.cpp index 763aa39..b944d71 100644 --- archiver/archiver.cpp +++ archiver/archiver.cpp @@ -1,27 +1,27 @@ /* - * - * Copyright 2018 Britanicus - * - * - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - */ + * + * Copyright 2018 Britanicus + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ // C++ Standard Library #include @@ -34,178 +34,205 @@ #include "libarchiveqt.h" /* To pretty print file sizes */ -QString formatSize( qint64 num ) { - QString total; - const qint64 kb = 1024; - const qint64 mb = 1024 * kb; - const qint64 gb = 1024 * mb; - const qint64 tb = 1024 * gb; - - if ( num >= tb ) { - total = QString( "%1 TiB" ).arg( QString::number( qreal( num ) / tb, 'f', 3 ) ); - } - else if ( num >= gb ) { - total = QString( "%1 GiB" ).arg( QString::number( qreal( num ) / gb, 'f', 2 ) ); - } - else if ( num >= mb ) { - total = QString( "%1 MiB" ).arg( QString::number( qreal( num ) / mb, 'f', 1 ) ); - } - else if ( num >= kb ) { - total = QString( "%1 KiB" ).arg( QString::number( qreal( num ) / kb, 'f', 1 ) ); - } - else { - total = QString( "%1 byte%2" ).arg( num ).arg( num > 1 ? "s": "" ); - } - - return total; +QString formatSize(qint64 num) +{ + QString total; + const qint64 kb = 1024; + const qint64 mb = 1024 * kb; + const qint64 gb = 1024 * mb; + const qint64 tb = 1024 * gb; + + if (num >= tb) + { + total = QString("%1 TiB").arg(QString::number(qreal(num) / tb, 'f', 3)); + } + else if (num >= gb) + { + total = QString("%1 GiB").arg(QString::number(qreal(num) / gb, 'f', 2)); + } + else if (num >= mb) + { + total = QString("%1 MiB").arg(QString::number(qreal(num) / mb, 'f', 1)); + } + else if (num >= kb) + { + total = QString("%1 KiB").arg(QString::number(qreal(num) / kb, 'f', 1)); + } + else + { + total = QString("%1 byte%2").arg(num).arg(num > 1 ? "s": ""); + } + + return total; } /* Pretty print the usage */ -void printUsage( const char *exec ) { - std::cout << "Archiver " ARCHIVEQT_VERSION_STR "\n" << std::endl; - - std::cout << "Usage:\n\t" << exec << " -c archive.xxx file1 file2 file3 ..." << std::endl; - std::cout << "\t" << exec << " -d archive.xxx output_dir" << std::endl; - std::cout << "\t" << exec << " -m archive.xxx member_name output_dir" << std::endl; - std::cout << "\t" << exec << " -l archive.xxx" << std::endl; +void printUsage(const char *exec) +{ + std::cout << "Archiver v" ARCHIVEQT_VERSION_STR "\n" << std::endl; + + std::cout << "Usage:\n\t" << exec << " -c archive.xxx file1 file2 file3 ..." << std::endl; + std::cout << "\t" << exec << " -d archive.xxx output_dir" << std::endl; + std::cout << "\t" << exec << " -m archive.xxx member_name output_dir" << std::endl; + std::cout << "\t" << exec << " -l archive.xxx" << std::endl; } -int main( int argc, char **argv ) { - /* Init the QApplication instance */ - QCoreApplication app( argc, argv ); - - /* - * - * We need three arguments at minimum - * 1. The program name (argv[ 0 ]) => Always existing - * 2. The switch: one of c, d, m or l: Tells the program what to do - * 3. The archive name to operate on (list or decompress) - * In case we are compressing or extracting a member, an additional argument is necessary - * - */ - if ( argc < 3 ) { - printUsage( argv[ 0 ] ); - return 1; - } - - /* Print help text or usage */ - else if ( (argc == 2) and (strcmp( argv[ 1 ], "-h" ) == 0) ) { - printUsage( argv[ 0 ] ); - return 0; - } - - /* Print help text or usage */ - else if ( (argc == 2) and (strcmp( argv[ 1 ], "--help" ) == 0) ) { - printUsage( argv[ 0 ] ); - return 0; - } - - /* Switch c, but no input files mentioned */ - else if ( (argc == 3) and (strcmp( argv[ 1 ], "-c" ) == 0) ) { - printUsage( argv[ 0 ] ); - - std::cout << "\nArchiver: ERROR: No input files specified." << std::endl; - return 1; - } - - /* Switch m, but no member name mentioned */ - else if ( (argc < 4) and (strcmp( argv[ 1 ], "-m" ) == 0) ) { - printUsage( argv[ 0 ] ); - - std::cout << "\nArchiver: ERROR: No member name specified." << std::endl; - return 1; - } - - /* Excess arguments */ - else if ( (argc > 3) and (strcmp( argv[ 1 ], "-l" ) == 0) ) { - printUsage( argv[ 0 ] ); - - std::cout << "\nArchiver: ERROR: Too many files specified" << std::endl; - return 1; - } - - /* No switch mentioned */ - else if ( (argc >= 3) and strcmp( argv[ 1 ], "-c" ) and strcmp( argv[ 1 ], "-d" ) and strcmp( argv[ 1 ], "-l" ) and strcmp( argv[ 1 ], "-m" ) ) { - printUsage( argv[ 0 ] ); - - std::cout << "\nArchiver: ERROR: You need to specify one of -c, -d, -m or -l" << std::endl; - return 1; - } - - /* Compress the input files argv[3+] into archive argv[2] */ - else if ( strcmp( argv[ 1 ], "-c" ) == 0 ) { - // Write archive code - LibArchiveQt *arc = new LibArchiveQt( argv[ 2 ] ); - - arc->updateInputFiles( app.arguments().mid( 3 ), LibArchiveQt::RelativeToCurrent ); - arc->createArchive(); - arc->waitForFinished(); - - return 0; - } - - /* Decompress the archive argv[2] optionally to argv[3] */ - else if ( strcmp( argv[ 1 ], "-d" ) == 0 ) { - // Read archive code - LibArchiveQt *arc = new LibArchiveQt( argv[ 2 ] ); - - if ( argc >= 4 ) { - arc->setDestination( argv[ 3 ] ); - } - - arc->extractArchive(); - arc->waitForFinished(); - - return 0; - } - - /* Decompress the member argv[3] optionally to argv[4] from archive argv[2] */ - else if ( strcmp( argv[ 1 ], "-m" ) == 0 ) { - // Read archive code - LibArchiveQt *arc = new LibArchiveQt( argv[ 2 ] ); - - if ( argc == 5 ) { - arc->setDestination( argv[ 3 ] ); - arc->extractMember( argv[ 4 ] ); - } - else { - arc->extractMember( argv[ 3 ] ); - } - - arc->waitForFinished(); - return 0; - } - - /* List archive argv[2] */ - else if ( strcmp( argv[ 1 ], "-l" ) == 0 ) { - // List archive code - LibArchiveQt *arc = new LibArchiveQt( argv[ 2 ] ); - - int length = 0; - Q_FOREACH (ArchiveEntry *ae, arc->listArchive() ) { - length = (ae->name.length() > length ? ae->name.length() : length); - } - - Q_FOREACH (ArchiveEntry *ae, arc->listArchive() ) { - if ( ae->type == AE_IFREG ) { - qDebug() << ae->name.rightJustified( length + 10 ).toLocal8Bit().data() << " " << formatSize( ae->size ).toLocal8Bit().data(); - } - - else { - qDebug() << ae->name.rightJustified( length + 10 ).toLocal8Bit().data(); - } - } - - arc->waitForFinished(); - - return 0; - } - - /* Print help text */ - else { - printUsage( argv[ 0 ] ); - return 0; - } +int main(int argc, char **argv) +{ + /* Init the QApplication instance */ + QCoreApplication app(argc, argv); + + /* + * + * We need three arguments at minimum + * 1. The program name (argv[ 0 ]) => Always existing + * 2. The switch: one of c, d, m or l: Tells the program what to do + * 3. The archive name to operate on (list or decompress) + * In case we are compressing or extracting a member, an additional argument is necessary + * + */ + if (argc < 3) + { + printUsage(argv[0]); + return 1; + } + + /* Print help text or usage */ + else if ((argc == 2) and (strcmp(argv[1], "-h") == 0)) + { + printUsage(argv[0]); + return 0; + } + + /* Print help text or usage */ + else if ((argc == 2) and (strcmp(argv[1], "--help") == 0)) + { + printUsage(argv[0]); + return 0; + } + + /* Switch c, but no input files mentioned */ + else if ((argc == 3) and (strcmp(argv[1], "-c") == 0)) + { + printUsage(argv[0]); + + std::cout << "\nArchiver: ERROR: No input files specified." << std::endl; + return 1; + } + + /* Switch m, but no member name mentioned */ + else if ((argc < 4) and (strcmp(argv[1], "-m") == 0)) + { + printUsage(argv[0]); + + std::cout << "\nArchiver: ERROR: No member name specified." << std::endl; + return 1; + } + + /* Excess arguments */ + else if ((argc > 3) and (strcmp(argv[1], "-l") == 0)) + { + printUsage(argv[0]); + + std::cout << "\nArchiver: ERROR: Too many files specified" << std::endl; + return 1; + } + + /* No switch mentioned */ + else if ((argc >= 3) and strcmp(argv[1], "-c") and strcmp(argv[1], "-d") and strcmp(argv[1], "-l") and strcmp(argv[1], "-m")) + { + printUsage(argv[0]); + + std::cout << "\nArchiver: ERROR: You need to specify one of -c, -d, -m or -l" << std::endl; + return 1; + } + + /* Compress the input files argv[3+] into archive argv[2] */ + else if (strcmp(argv[1], "-c") == 0) + { + // Write archive code + LibArchiveQt *arc = new LibArchiveQt(argv[2]); + + arc->updateInputFiles(app.arguments().mid(3), LibArchiveQt::RelativeToCurrent); + arc->createArchive(); + arc->waitForFinished(); + + return 0; + } + + /* Decompress the archive argv[2] optionally to argv[3] */ + else if (strcmp(argv[1], "-d") == 0) + { + // Read archive code + LibArchiveQt *arc = new LibArchiveQt(argv[2]); + + if (argc >= 4) + { + arc->setDestination(argv[3]); + } + + arc->extractArchive(); + arc->waitForFinished(); + + return 0; + } + + /* Decompress the member argv[3] optionally to argv[4] from archive argv[2] */ + else if (strcmp(argv[1], "-m") == 0) + { + // Read archive code + LibArchiveQt *arc = new LibArchiveQt(argv[2]); + + if (argc == 5) + { + arc->setDestination(argv[3]); + arc->extractMember(argv[4]); + } + else + { + arc->extractMember(argv[3]); + } + + arc->waitForFinished(); + return 0; + } + + /* List archive argv[2] */ + else if (strcmp(argv[1], "-l") == 0) + { + // List archive code + LibArchiveQt *arc = new LibArchiveQt(argv[2]); + + int length = 0; + Q_FOREACH (ArchiveEntry *ae, arc->listArchive()) + { + length = (ae->name.length() > length ? ae->name.length() : length); + } + + Q_FOREACH (ArchiveEntry *ae, arc->listArchive()) + { + if (ae->type == AE_IFREG) + { + qDebug() << ae->name.rightJustified(length + 10).toLocal8Bit().data() << " " << formatSize(ae->size).toLocal8Bit().data(); + } + + else + { + qDebug() << ae->name.rightJustified(length + 10).toLocal8Bit().data(); + } + } + + arc->waitForFinished(); + + return 0; + } + + /* Print help text */ + else + { + printUsage(argv[0]); + return 0; + } } diff --git debian/changelog debian/changelog index fab2911..35844c3 100644 --- debian/changelog +++ debian/changelog @@ -1,10 +1,3 @@ -libarchiveqt (2.0.8) unstable; urgency=medium - * Remove Qt4 support - * Remove Qt5/Qt6 deprecation warnings - * Bump version number - - -- Marcus Britanicus Thu, 04 Mar 2023 10:00:00 +0530 - libarchiveqt (2.0.7) unstable; urgency=medium * Fix meson-related bugs * Bump version number diff --git lib/Global.hpp lib/Global.hpp index f6027f6..ba6d28a 100644 --- lib/Global.hpp +++ lib/Global.hpp @@ -1,6 +1,8 @@ -/** - * Global.hpp - Globally used header - **/ +/* + * + * Global.hpp - Globally used header + * +*/ #pragma once @@ -22,13 +24,13 @@ #include #if QT_VERSION >= 0x050000 -#include -#include -#include + #include + #include + #include #else -#include -#include -#include + #include + #include + #include #endif static QMimeDatabase mimeDb; diff --git lib/LibArchive.cpp lib/LibArchive.cpp index c58f242..abfe81b 100644 --- lib/LibArchive.cpp +++ lib/LibArchive.cpp @@ -1,19 +1,27 @@ -/** - * Copyright 2018 Britanicus - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - **/ +/* + * + * Copyright 2018 Britanicus + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ #include "Global.hpp" #include "libarchiveqt.h" @@ -34,1588 +42,1793 @@ extern "C" { #include #include -inline static bool isDir( QString path ) { - struct stat statbuf; - - if ( stat( path.toLocal8Bit().data(), &statbuf ) == 0 ) { - if ( S_ISDIR( statbuf.st_mode ) ) { - return true; - } - - else { - return false; - } - } - - else { - return false; - } +inline static bool isDir(QString path) +{ + struct stat statbuf; + + if (stat(path.toLocal8Bit().data(), &statbuf) == 0) + { + if (S_ISDIR(statbuf.st_mode)) + { + return true; + } + + else + { + return false; + } + } + + else + { + return false; + } } -inline static QStringList recDirWalk( QString path ) { - QStringList fileList; +inline static QStringList recDirWalk(QString path) +{ + QStringList fileList; - if ( not isDir( path ) ) { - return fileList; - } + if (not isDir(path)) + { + return fileList; + } - QDirIterator it( path, QDir::AllEntries | QDir::System | QDir::NoDotAndDotDot | QDir::Hidden, QDirIterator::Subdirectories ); + QDirIterator it(path, QDir::AllEntries | QDir::System | QDir::NoDotAndDotDot | QDir::Hidden, QDirIterator::Subdirectories); - while ( it.hasNext() ) { - it.next(); + while (it.hasNext()) + { + it.next(); + if (it.fileInfo().isFile()) + { + fileList.append(it.fileInfo().filePath()); + } + } - if ( it.fileInfo().isFile() ) { - fileList.append( it.fileInfo().filePath() ); - } - } - - return fileList; + return fileList; } -inline static QString dirName( QString path ) { - while ( path.contains( "//" ) ) { - path = path.replace( "//", "/" ); - } +inline static QString dirName(QString path) +{ + while (path.contains("//")) + { + path = path.replace("//", "/"); + } - if ( path.endsWith( "/" ) ) { - path.chop( 1 ); - } + if (path.endsWith("/")) + { + path.chop(1); + } - char *dupPath = strdup( path.toLocal8Bit().constData() ); - QString dirPth = QString( dirname( dupPath ) ) + "/"; + char *dupPath = strdup(path.toLocal8Bit().constData()); + QString dirPth = QString(dirname(dupPath)) + "/"; - free( dupPath ); + free(dupPath); - return (dirPth == "//" ? "/" : dirPth); + return(dirPth == "//" ? "/" : dirPth); } -inline static QString baseName( QString path ) { - while ( path.contains( "//" ) ) { - path = path.replace( "//", "/" ); - } +inline static QString baseName(QString path) +{ + while (path.contains("//")) + { + path = path.replace("//", "/"); + } - if ( path.endsWith( "/" ) ) { - path.chop( 1 ); - } + if (path.endsWith("/")) + { + path.chop(1); + } - char *dupPath = strdup( path.toLocal8Bit().constData() ); - QString basePth = QString( basename( dupPath ) ); + char *dupPath = strdup(path.toLocal8Bit().constData()); + QString basePth = QString(basename(dupPath)); - free( dupPath ); + free(dupPath); - return basePth; + return basePth; } -inline static bool exists( QString path ) { - return not access( path.toLocal8Bit().constData(), F_OK ); +inline static bool exists(QString path) +{ + return not access(path.toLocal8Bit().constData(), F_OK); } -inline static int mkpath( QString path, mode_t mode ) { - /* Root always exists */ - if ( path == "/" ) { - return 0; - } +inline static int mkpath(QString path, mode_t mode) +{ + /* Root always exists */ + if (path == "/") + { + return 0; + } - /* If the directory exists, thats okay for us */ - if ( exists( path ) ) { - return 0; - } + /* If the directory exists, thats okay for us */ + if (exists(path)) + { + return 0; + } - /* If the path is absolute, remove the leading '/' */ - if ( path.startsWith( '/' ) ) { - path.remove( 0, 1 ); - } + /* If the path is absolute, remove the leading '/' */ + if (path.startsWith('/')) + { + path.remove(0, 1); + } - mkpath( dirName( path ), mode ); + mkpath(dirName(path), mode); - return mkdir( path.toLocal8Bit().constData(), mode ); + return mkdir(path.toLocal8Bit().constData(), mode); } -inline static QString longestPath( QStringList& dirs ) { - QStringList paths; +inline static QString longestPath(QStringList& dirs) +{ + QStringList paths; - Q_FOREACH (QString file, dirs) { - paths << QFileInfo( file ).absoluteFilePath(); - } + Q_FOREACH (QString file, dirs) + { + paths << QFileInfo(file).absoluteFilePath(); + } - /* Get shortest path: Shortest path is the one with least number of '/' */ - QString shortest = paths.at( 0 ); - int count = 10240; + /* Get shortest path: Shortest path is the one with least number of '/' */ + QString shortest = paths.at(0); + int count = 10240; - Q_FOREACH (QString path, paths) { - if ( path.count( "/" ) < count ) { - count = path.count( "/" ); - shortest = path; - } - } + Q_FOREACH (QString path, paths) + { + if (path.count("/") < count) + { + count = path.count("/"); + shortest = path; + } + } - /* Remove the trailing '/' */ - if ( shortest.endsWith( "/" ) ) { - shortest.chop( 1 ); - } + /* Remove the trailing '/' */ + if (shortest.endsWith("/")) + { + shortest.chop(1); + } - QFileInfo sDir( shortest ); + QFileInfo sDir(shortest); - while ( paths.filter( sDir.absoluteFilePath() ).length() != paths.length() ) { - if ( sDir.absoluteFilePath() == "/" ) { - break; - } + while (paths.filter(sDir.absoluteFilePath()).count() != paths.count()) + { + if (sDir.absoluteFilePath() == "/") + { + break; + } - sDir = QFileInfo( sDir.absolutePath() ); - } + sDir = QFileInfo(sDir.absolutePath()); + } - return sDir.absoluteFilePath(); + return sDir.absoluteFilePath(); } -LibArchiveQt::LibArchiveQt( QString archive ) { - readDone = false; - isRunning = false; - mJob = NoJob; - extractedMember = QString(); - mExitStatus = 0; // 0 - Good, 1 - Bad +LibArchiveQt::LibArchiveQt(QString archive) +{ + readDone = false; + isRunning = false; + mJob = NoJob; + extractedMember = QString(); + mExitStatus = 0; // 0 - Good, 1 - Bad - archiveName = QDir( archive ).absolutePath(); + archiveName = QDir(archive).absolutePath(); - setFilterFormat( mimeDb.mimeTypeForFile( archiveName ) ); + setFilterFormat(mimeDb.mimeTypeForFile(archiveName)); } -QString LibArchiveQt::suffix( QString archiveName ) { - QMimeType mType = mimeDb.mimeTypeForFile( archiveName ); - - if ( mType == mimeDb.mimeTypeForFile( "file.cpio" ) ) { - return ".cpio"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.shar" ) ) { - return ".shar"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar" ) ) { - return ".tar"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.gz" ) ) { - return (archiveName.endsWith( ".tar.gz" ) ? ".tar.gz" : ".tgz"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.grz" ) ) { - return (archiveName.endsWith( ".tar.grz" ) ? ".tar.grz" : ".tgrz"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.xz" ) ) { - return (archiveName.endsWith( ".tar.xz" ) ? ".tar.xz" : ".txz"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lzo" ) ) { - return (archiveName.endsWith( ".tar.lzo" ) ? ".tar.lzo" : ".tlzo"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lzma" ) ) { - return (archiveName.endsWith( ".tar.lzma" ) ? ".tar.lzma" : ".tlzma"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lz" ) ) { - return (archiveName.endsWith( ".tar.lz" ) ? ".tar.lz" : ".tlz"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lrz" ) ) { - return (archiveName.endsWith( ".tar.lrz" ) ? ".tar.lrz" : ".tlrz"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lz4" ) ) { - return (archiveName.endsWith( ".tar.lz4" ) ? ".tar.lz4" : ".tlzo4"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.bz2" ) ) { - return (archiveName.endsWith( ".tar.bz2" ) ? ".tar.bz2" : ".tbz2"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.Z" ) ) { - return (archiveName.endsWith( ".tar.Z" ) ? ".tar.Z" : ".tZ"); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.iso" ) ) { - return ".iso"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.zip" ) ) { - return ".zip"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.ar" ) ) { - return ".ar"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.xar" ) ) { - return ".xar"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.7z" ) ) { - return ".7z"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lz" ) ) { - return ".lz"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lz4" ) ) { - return ".lz4"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.uu" ) ) { - return ".uu"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lzo" ) ) { - return ".lzo"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.gz" ) ) { - return ".gz"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.bz2" ) ) { - return ".bz2"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lzma" ) ) { - return ".lzma"; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.xz" ) ) { - return ".xz"; - } - - return ""; +QString LibArchiveQt::suffix(QString archiveName) +{ + QMimeType mType = mimeDb.mimeTypeForFile(archiveName); + + if (mType == mimeDb.mimeTypeForFile("file.cpio")) + { + return ".cpio"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.shar")) + { + return ".shar"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar")) + { + return ".tar"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.gz")) + { + return(archiveName.endsWith(".tar.gz") ? ".tar.gz" : ".tgz"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.grz")) + { + return(archiveName.endsWith(".tar.grz") ? ".tar.grz" : ".tgrz"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.xz")) + { + return(archiveName.endsWith(".tar.xz") ? ".tar.xz" : ".txz"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.lzo")) + { + return(archiveName.endsWith(".tar.lzo") ? ".tar.lzo" : ".tlzo"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.lzma")) + { + return(archiveName.endsWith(".tar.lzma") ? ".tar.lzma" : ".tlzma"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.lz")) + { + return(archiveName.endsWith(".tar.lz") ? ".tar.lz" : ".tlz"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.lrz")) + { + return(archiveName.endsWith(".tar.lrz") ? ".tar.lrz" : ".tlrz"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.lz4")) + { + return(archiveName.endsWith(".tar.lz4") ? ".tar.lz4" : ".tlzo4"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.bz2")) + { + return(archiveName.endsWith(".tar.bz2") ? ".tar.bz2" : ".tbz2"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.Z")) + { + return(archiveName.endsWith(".tar.Z") ? ".tar.Z" : ".tZ"); + } + + else if (mType == mimeDb.mimeTypeForFile("file.iso")) + { + return ".iso"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.zip")) + { + return ".zip"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.ar")) + { + return ".ar"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.xar")) + { + return ".xar"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.7z")) + { + return ".7z"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lz")) + { + return ".lz"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lz4")) + { + return ".lz4"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.uu")) + { + return ".uu"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lzo")) + { + return ".lzo"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.gz")) + { + return ".gz"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.bz2")) + { + return ".bz2"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lzma")) + { + return ".lzma"; + } + + else if (mType == mimeDb.mimeTypeForFile("file.xz")) + { + return ".xz"; + } + + return ""; } -QStringList LibArchiveQt::supportedFormats() { - QStringList supported; - - supported << mimeDb.mimeTypeForFile( "file.cpio" ).name(); - supported << mimeDb.mimeTypeForFile( "file.shar" ).name(); - supported << mimeDb.mimeTypeForFile( "file.tar" ).name(); - supported << mimeDb.mimeTypeForFile( "file.tar.gz" ).name(); - supported << mimeDb.mimeTypeForFile( "file.tar.grz" ).name(); - supported << mimeDb.mimeTypeForFile( "file.tar.xz" ).name(); - supported << mimeDb.mimeTypeForFile( "file.tar.lzma" ).name(); - supported << mimeDb.mimeTypeForFile( "file.tar.lz4" ).name(); - supported << mimeDb.mimeTypeForFile( "file.tar.bz2" ).name(); - supported << mimeDb.mimeTypeForFile( "file.tar.Z" ).name(); - supported << mimeDb.mimeTypeForFile( "file.iso" ).name(); - supported << mimeDb.mimeTypeForFile( "file.zip" ).name(); - supported << mimeDb.mimeTypeForFile( "file.ar" ).name(); - supported << mimeDb.mimeTypeForFile( "file.xar" ).name(); - supported << mimeDb.mimeTypeForFile( "file.7z" ).name(); - supported << mimeDb.mimeTypeForFile( "file.lz" ).name(); - supported << mimeDb.mimeTypeForFile( "file.lz4" ).name(); - supported << mimeDb.mimeTypeForFile( "file.uu" ).name(); - supported << mimeDb.mimeTypeForFile( "file.lzo" ).name(); - supported << mimeDb.mimeTypeForFile( "file.gz" ).name(); - supported << mimeDb.mimeTypeForFile( "file.bz2" ).name(); - supported << mimeDb.mimeTypeForFile( "file.lzma" ).name(); - supported << mimeDb.mimeTypeForFile( "file.xz" ).name(); - - QString binary; - -#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 ) - QStringList exeLocs = QString::fromLocal8Bit( qgetenv( "PATH" ) ).split( ":", Qt::SkipEmptyParts ); +QStringList LibArchiveQt::supportedFormats() +{ + QStringList supported; + + supported << mimeDb.mimeTypeForFile("file.cpio").name(); + supported << mimeDb.mimeTypeForFile("file.shar").name(); + supported << mimeDb.mimeTypeForFile("file.tar").name(); + supported << mimeDb.mimeTypeForFile("file.tar.gz").name(); + supported << mimeDb.mimeTypeForFile("file.tar.grz").name(); + supported << mimeDb.mimeTypeForFile("file.tar.xz").name(); + supported << mimeDb.mimeTypeForFile("file.tar.lzma").name(); + supported << mimeDb.mimeTypeForFile("file.tar.lz4").name(); + supported << mimeDb.mimeTypeForFile("file.tar.bz2").name(); + supported << mimeDb.mimeTypeForFile("file.tar.Z").name(); + supported << mimeDb.mimeTypeForFile("file.iso").name(); + supported << mimeDb.mimeTypeForFile("file.zip").name(); + supported << mimeDb.mimeTypeForFile("file.ar").name(); + supported << mimeDb.mimeTypeForFile("file.xar").name(); + supported << mimeDb.mimeTypeForFile("file.7z").name(); + supported << mimeDb.mimeTypeForFile("file.lz").name(); + supported << mimeDb.mimeTypeForFile("file.lz4").name(); + supported << mimeDb.mimeTypeForFile("file.uu").name(); + supported << mimeDb.mimeTypeForFile("file.lzo").name(); + supported << mimeDb.mimeTypeForFile("file.gz").name(); + supported << mimeDb.mimeTypeForFile("file.bz2").name(); + supported << mimeDb.mimeTypeForFile("file.lzma").name(); + supported << mimeDb.mimeTypeForFile("file.xz").name(); + + QString binary; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QStringList exeLocs = QString::fromLocal8Bit(qgetenv("PATH")).split(":", Qt::SkipEmptyParts); #else - QStringList exeLocs = QString::fromLocal8Bit( qgetenv( "PATH" ) ).split( ":", QString::SkipEmptyParts ); + QStringList exeLocs = QString::fromLocal8Bit(qgetenv("PATH")).split(":", QString::SkipEmptyParts); #endif - QString lzop = mimeDb.mimeTypeForFile( "file.lzo" ).name(); - QString lzip = mimeDb.mimeTypeForFile( "file.lz" ).name(); - QString lrzip = mimeDb.mimeTypeForFile( "file.lrz" ).name(); - - Q_FOREACH (QString loc, exeLocs) { - if ( exists( loc + "/lzip" ) and not supported.contains( lzip ) ) { - supported << lzip; - } - - if ( exists( loc + "/lzop" ) and not supported.contains( lzop ) ) { - supported << lzop; - } - - if ( exists( loc + "/lrzip" ) and not supported.contains( lrzip ) ) { - supported << lrzip; - } - } - - return supported; + QString lzop = mimeDb.mimeTypeForFile("file.lzo").name(); + QString lzip = mimeDb.mimeTypeForFile("file.lz").name(); + QString lrzip = mimeDb.mimeTypeForFile("file.lrz").name(); + + Q_FOREACH (QString loc, exeLocs) + { + if (exists(loc + "/lzip") and not supported.contains(lzip)) + { + supported << lzip; + } + + if (exists(loc + "/lzop") and not supported.contains(lzop)) + { + supported << lzop; + } + + if (exists(loc + "/lrzip") and not supported.contains(lrzip)) + { + supported << lrzip; + } + } + + return supported; } -void LibArchiveQt::createArchive() { - mJob = CreateArchive; - isRunning = true; +void LibArchiveQt::createArchive() +{ + mJob = CreateArchive; + isRunning = true; - start(); + start(); } -void LibArchiveQt::extractArchive() { - mJob = ExtractArchive; - isRunning = true; +void LibArchiveQt::extractArchive() +{ + mJob = ExtractArchive; + isRunning = true; - start(); + start(); } -void LibArchiveQt::extractMember( QString memberName ) { - extractedMember = memberName; - mJob = ExtractMember; - isRunning = true; +void LibArchiveQt::extractMember(QString memberName) +{ + extractedMember = memberName; + mJob = ExtractMember; + isRunning = true; - start(); + start(); } -ArchiveEntries LibArchiveQt::listArchive() { - if ( readDone ) { - return memberList; - } +ArchiveEntries LibArchiveQt::listArchive() +{ + if (readDone) + { + return memberList; + } - memberList.clear(); + memberList.clear(); - struct archive *a; - struct archive_entry *entry; - int r; + struct archive *a; + struct archive_entry *entry; + int r; - // Source Archive - a = archive_read_new(); - archive_read_support_format_all( a ); - archive_read_support_format_raw( a ); - archive_read_support_filter_all( a ); + // Source Archive + a = archive_read_new(); + archive_read_support_format_all(a); + archive_read_support_format_raw(a); + archive_read_support_filter_all(a); - if ( (r = archive_read_open_filename( a, archiveName.toUtf8().data(), 10240 ) ) ) { - qDebug() << "[Error]" << archive_error_string( a ); - readDone = true; - return ArchiveEntries(); - } + if ((r = archive_read_open_filename(a, archiveName.toUtf8().data(), 10240))) + { + qDebug() << "[Error]" << archive_error_string(a); + readDone = true; + return ArchiveEntries(); + } - while ( true ) { - r = archive_read_next_header( a, &entry ); + while (true) + { + r = archive_read_next_header(a, &entry); - if ( r == ARCHIVE_EOF ) { - break; - } + if (r == ARCHIVE_EOF) + { + break; + } - if ( r < ARCHIVE_OK ) { - qDebug() << archive_error_string( a ); - } + if (r < ARCHIVE_OK) + { + qDebug() << archive_error_string(a); + } - ArchiveEntry *ae = new ArchiveEntry; - ae->name = archive_entry_pathname( entry ); - ae->size = archive_entry_size( entry ); - ae->type = archive_entry_filetype( entry ); - memcpy( &ae->info, archive_entry_stat( entry ), sizeof(struct stat) ); + ArchiveEntry *ae = new ArchiveEntry; + ae->name = archive_entry_pathname(entry); + ae->size = archive_entry_size(entry); + ae->type = archive_entry_filetype(entry); + memcpy(&ae->info, archive_entry_stat(entry), sizeof(struct stat)); - memberList << ae; - } + memberList << ae; + } - archive_read_close( a ); - archive_read_free( a ); + archive_read_close(a); + archive_read_free(a); - readDone = true; + readDone = true; - return memberList; + return memberList; } -int LibArchiveQt::exitStatus() { - return mExitStatus; +int LibArchiveQt::exitStatus() +{ + return mExitStatus; } -void LibArchiveQt::updateInputFiles( QStringList inFiles, LibArchiveQt::InputFileMode inMode ) { - if ( not inFiles.length() ) { - return; - } - - /* First get the absolute filenames */ - Q_FOREACH (QString file, inFiles) { - if ( isDir( file ) ) { - updateInputFiles( recDirWalk( file ), inMode ); - } - - else { - QFileInfo info( file ); - switch ( inMode ) { - case AbsolutePath: { - inputList.insert( info.absoluteFilePath(), info.absoluteFilePath() ); - break; - } - - case RelativeToRoot: { - inputList.insert( info.absoluteFilePath(), QDir::root().relativeFilePath( info.absoluteFilePath() ) ); - break; - } - - case RelativeToHome: { - QString relPath = QDir::home().relativeFilePath( info.absoluteFilePath() ); - while ( relPath.startsWith( "../" ) ) { - relPath.remove( 0, 3 ); - } - - inputList.insert( info.absoluteFilePath(), relPath ); - break; - } - - case RelativeToCurrent: { - QString relPath = QDir::current().relativeFilePath( info.absoluteFilePath() ); - while ( relPath.startsWith( "../" ) ) { - relPath.remove( 0, 3 ); - } - - inputList.insert( info.absoluteFilePath(), relPath ); - break; - } - - case RelativeToWorkDir: { - /* If @src is empty, set it to root */ - src = (src.isEmpty() ? "/" : src); - - QString relPath = QDir( src ).relativeFilePath( info.absoluteFilePath() ); - while ( relPath.startsWith( "../" ) ) { - relPath.remove( 0, 3 ); - } - - inputList.insert( info.absoluteFilePath(), relPath ); - break; - } - - case CommonRelativePath: { - QString common; - - if ( inFiles.length() == 1 ) { - common = dirName( inFiles.at( 0 ) ); - } - - else { - common = longestPath( inFiles ); - } - - QString relPath = QDir( common ).relativeFilePath( info.absoluteFilePath() ); - while ( relPath.startsWith( "../" ) ) { - relPath.remove( 0, 3 ); - } - - inputList.insert( info.absoluteFilePath(), relPath ); - break; - } - - default: { - QString relPath = QDir::current().relativeFilePath( info.absoluteFilePath() ); - while ( relPath.startsWith( "../" ) ) { - relPath.remove( 0, 3 ); - } - - inputList.insert( info.absoluteFilePath(), relPath ); - break; - } - } - } - } +void LibArchiveQt::updateInputFiles(QStringList inFiles, LibArchiveQt::InputFileMode inMode) +{ + if (not inFiles.count()) + { + return; + } + + /* First get the absolute filenames */ + Q_FOREACH (QString file, inFiles) + { + if (isDir(file)) + { + updateInputFiles(recDirWalk(file), inMode); + } + + else + { + QFileInfo info(file); + switch (inMode) + { + case AbsolutePath: + inputList.insert(info.absoluteFilePath(), info.absoluteFilePath()); + break; + + case RelativeToRoot: + inputList.insert(info.absoluteFilePath(), QDir::root().relativeFilePath(info.absoluteFilePath())); + break; + + case RelativeToHome: + { + QString relPath = QDir::home().relativeFilePath(info.absoluteFilePath()); + while (relPath.startsWith("../")) + { + relPath.remove(0, 3); + } + + inputList.insert(info.absoluteFilePath(), relPath); + break; + } + + case RelativeToCurrent: + { + QString relPath = QDir::current().relativeFilePath(info.absoluteFilePath()); + while (relPath.startsWith("../")) + { + relPath.remove(0, 3); + } + + inputList.insert(info.absoluteFilePath(), relPath); + break; + } + + case RelativeToWorkDir: + { + /* If @src is empty, set it to root */ + src = (src.isEmpty() ? "/" : src); + + QString relPath = QDir(src).relativeFilePath(info.absoluteFilePath()); + while (relPath.startsWith("../")) + { + relPath.remove(0, 3); + } + + inputList.insert(info.absoluteFilePath(), relPath); + break; + } + + case CommonRelativePath: + { + QString common; + if (inFiles.count() == 1) + { + common = dirName(inFiles.at(0)); + } + + else + { + common = longestPath(inFiles); + } + + QString relPath = QDir(common).relativeFilePath(info.absoluteFilePath()); + while (relPath.startsWith("../")) + { + relPath.remove(0, 3); + } + + inputList.insert(info.absoluteFilePath(), relPath); + break; + } + + default: + { + QString relPath = QDir::current().relativeFilePath(info.absoluteFilePath()); + while (relPath.startsWith("../")) + { + relPath.remove(0, 3); + } + + inputList.insert(info.absoluteFilePath(), relPath); + break; + } + } + } + } } -void LibArchiveQt::setWorkingDir( QString wDir ) { - src = QString( wDir ); +void LibArchiveQt::setWorkingDir(QString wDir) +{ + src = QString(wDir); } -void LibArchiveQt::setDestination( QString path ) { - /* - * - * @p path will be a absolute. - * So QDir we construct will be home path - * - */ - - dest = QString( path ); +void LibArchiveQt::setDestination(QString path) +{ + /* + * + * @p path will be a absolute. + * So QDir we construct will be home path + * + */ - if ( not QFileInfo( QDir( dest ).absolutePath() ).exists() ) { - mkpath( path, 0755 ); - } + dest = QString(path); + if (not QFileInfo(QDir(dest).absolutePath()).exists()) + { + mkpath(path, 0755); + } - qDebug() << "Extracting to:" << dest; + qDebug() << "Extracting to:" << dest; } -void LibArchiveQt::waitForFinished() { - if ( not isRunning ) { - return; - } +void LibArchiveQt::waitForFinished() +{ + if (not isRunning) + { + return; + } - QEventLoop eventLoop; + QEventLoop eventLoop; #if QT_VERSION >= 0x050000 - connect( this, &LibArchiveQt::jobFailed, &eventLoop, &QEventLoop::quit ); - connect( this, &LibArchiveQt::jobComplete, &eventLoop, &QEventLoop::quit ); + connect(this, &LibArchiveQt::jobFailed, &eventLoop, &QEventLoop::quit); + connect(this, &LibArchiveQt::jobComplete, &eventLoop, &QEventLoop::quit); #else - connect( this, SIGNAL(jobFailed()), &eventLoop, SLOT( quit() ) ); - connect( this, SIGNAL(jobComplete()), &eventLoop, SLOT( quit() ) ); + connect(this, SIGNAL(jobFailed()), &eventLoop, SLOT(quit())); + connect(this, SIGNAL(jobComplete()), &eventLoop, SLOT(quit())); #endif - eventLoop.exec(); + eventLoop.exec(); } -void LibArchiveQt::run() { - switch ( mJob ) { - case CreateArchive: { - if ( doCreateArchive() ) { - mExitStatus = 0; - emit jobComplete(); - } - - else { - mExitStatus = 1; - emit jobFailed(); - } - - isRunning = false; - return; - } - - case ExtractArchive: { - if ( doExtractArchive() ) { - mExitStatus = 0; - emit jobComplete(); - } - - else { - mExitStatus = 1; - emit jobFailed(); - } - - isRunning = false; - return; - } - - case ExtractMember: { - if ( doExtractMember( extractedMember ) ) { - mExitStatus = 0; - emit jobComplete(); - } - - else { - mExitStatus = 1; - emit jobFailed(); - } - - isRunning = false; - return; - } - - case ListArchive: { - /* Nothing to run in the thread */ - return; - } - } +void LibArchiveQt::run() +{ + switch (mJob) + { + case CreateArchive: + if (doCreateArchive()) + { + mExitStatus = 0; + emit jobComplete(); + } + + else + { + mExitStatus = 1; + emit jobFailed(); + } + + isRunning = false; + return; + + case ExtractArchive: + if (doExtractArchive()) + { + mExitStatus = 0; + emit jobComplete(); + } + + else + { + mExitStatus = 1; + emit jobFailed(); + } + + isRunning = false; + return; + + case ExtractMember: + if (doExtractMember(extractedMember)) + { + mExitStatus = 0; + emit jobComplete(); + } + + else + { + mExitStatus = 1; + emit jobFailed(); + } + + isRunning = false; + return; + + case ListArchive: + /* Nothing to run in the thread */ + return; + } } -bool LibArchiveQt::doCreateArchive() { - struct archive *a; - struct archive_entry *entry; - struct stat st; - char buff[ 8192 ]; - int len; - int fd; - int r = ARCHIVE_OK; - int errors = 0; - int processed = 0; - - /* Prepare the workingDir */ - if ( src.isEmpty() ) { - src = "/"; - } - - a = archive_write_new(); - - // Depend on the format provided by the user - r |= archive_write_set_format( a, mArchiveFormat ); - r |= archive_write_add_filter( a, mArchiveFilter ); - - if ( r < ARCHIVE_OK ) { - qDebug() << "Cannot use the input filter/format."; - return false; - } - - r = archive_write_open_filename( a, archiveName.toUtf8().data() ); - - if ( r < ARCHIVE_OK ) { - qDebug() << "Unable to write file for writing."; - return false; - } - - Q_FOREACH (QString file, inputList.keys() ) { - char *filename = new char[ file.length() + 1 ]; - strcpy( filename, file.toUtf8().data() ); - - if ( stat( filename, &st ) != 0 ) { - errors++; - printf( "[Error %d]: %s: %s\n", errno, strerror( errno ), filename ); - continue; - } - - char *arcPath = new char[ file.length() + 1 ]; - strcpy( arcPath, inputList.value( file ).toUtf8().data() ); - - entry = archive_entry_new(); - archive_entry_set_pathname( entry, arcPath ); - archive_entry_set_size( entry, st.st_size ); - archive_entry_set_filetype( entry, st.st_mode ); - archive_entry_set_perm( entry, st.st_mode ); - - archive_write_header( a, entry ); - - // Perform the write - fd = open( filename, O_RDONLY ); - len = read( fd, buff, sizeof(buff) ); - while ( len > 0 ) { - archive_write_data( a, buff, len ); - len = read( fd, buff, sizeof(buff) ); - } - close( fd ); - archive_entry_free( entry ); - - processed++; - - emit progress( processed * 100 / inputList.count() ); - qApp->processEvents(); - } - - archive_write_close( a ); - archive_write_free( a ); - - return (errors ? false : true); +bool LibArchiveQt::doCreateArchive() +{ + struct archive *a; + struct archive_entry *entry; + struct stat st; + char buff[8192]; + int len; + int fd; + int r = ARCHIVE_OK; + int errors = 0; + int processed = 0; + + /* Prepare the workingDir */ + if (src.isEmpty()) + { + src = "/"; + } + + a = archive_write_new(); + + // Depend on the format provided by the user + r |= archive_write_set_format(a, mArchiveFormat); + r |= archive_write_add_filter(a, mArchiveFilter); + if (r < ARCHIVE_OK) + { + qDebug() << "Cannot use the input filter/format."; + return false; + } + + r = archive_write_open_filename(a, archiveName.toUtf8().data()); + if (r < ARCHIVE_OK) + { + qDebug() << "Unable to write file for writing."; + return false; + } + + Q_FOREACH (QString file, inputList.keys()) + { + char *filename = new char[file.count() + 1]; + strcpy(filename, file.toUtf8().data()); + + if (stat(filename, &st) != 0) + { + errors++; + printf("[Error %d]: %s: %s\n", errno, strerror(errno), filename); + continue; + } + + char *arcPath = new char[file.count() + 1]; + strcpy(arcPath, inputList.value(file).toUtf8().data()); + + entry = archive_entry_new(); + archive_entry_set_pathname(entry, arcPath); + archive_entry_set_size(entry, st.st_size); + archive_entry_set_filetype(entry, st.st_mode); + archive_entry_set_perm(entry, st.st_mode); + + archive_write_header(a, entry); + + // Perform the write + fd = open(filename, O_RDONLY); + len = read(fd, buff, sizeof(buff)); + while (len > 0) + { + archive_write_data(a, buff, len); + len = read(fd, buff, sizeof(buff)); + } + close(fd); + archive_entry_free(entry); + + processed++; + + emit progress(processed * 100 / inputList.count()); + qApp->processEvents(); + } + + archive_write_close(a); + archive_write_free(a); + + return(errors ? false : true); } -bool LibArchiveQt::doExtractArchive() { - if ( archiveType == None ) { - return false; - } - - // Change to the target directory - char srcDir[ 10240 ] = { 0 }; +bool LibArchiveQt::doExtractArchive() +{ + if (archiveType == None) + { + return false; + } - getcwd( srcDir, 10240 ); + // Change to the target directory + char srcDir[10240] = { 0 }; - if ( not dest.isEmpty() ) { - int ret = chdir( dest.toUtf8().data() ); + getcwd(srcDir, 10240); - if ( ret ) { - qDebug() << "chdir() failed:" << errno; - } - } + if (not dest.isEmpty()) + { + int ret = chdir(dest.toUtf8().data()); + if (ret) + { + qDebug() << "chdir() failed:" << errno; + } + } - if ( archiveType == Single ) { - QMimeType mType = mimeDb.mimeTypeForFile( archiveName ); + if (archiveType == Single) + { + QMimeType mType = mimeDb.mimeTypeForFile(archiveName); - if ( mType == mimeDb.mimeTypeForFile( "file.lz" ) ) { - /* LZip Extractor */ + if (mType == mimeDb.mimeTypeForFile("file.lz")) + { + /* LZip Extractor */ - QString lzip; + QString lzip; -#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 ) - QStringList exeLocs = QString::fromLocal8Bit( qgetenv( "PATH" ) ).split( ":", Qt::SkipEmptyParts ); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QStringList exeLocs = QString::fromLocal8Bit(qgetenv("PATH")).split(":", Qt::SkipEmptyParts); #else - QStringList exeLocs = QString::fromLocal8Bit( qgetenv( "PATH" ) ).split( ":", QString::SkipEmptyParts ); + QStringList exeLocs = QString::fromLocal8Bit(qgetenv("PATH")).split(":", QString::SkipEmptyParts); #endif - Q_FOREACH (QString loc, exeLocs) { - if ( exists( loc + "/lzip" ) ) { - lzip = loc + "/lzip"; - break; - } - } - - if ( not lzip.length() ) { - qDebug() << "External program lzip not found."; - return false; - } - - struct archive *a; - struct archive *ext; - struct archive_entry *entry; - int flags; - - int r = ARCHIVE_OK; - - /* Select which attributes we want to restore. */ - flags = ARCHIVE_EXTRACT_TIME; - flags |= ARCHIVE_EXTRACT_PERM; - flags |= ARCHIVE_EXTRACT_ACL; - flags |= ARCHIVE_EXTRACT_FFLAGS; - - // Source Archive - a = archive_read_new(); - r |= archive_read_support_format_raw( a ); - r |= archive_read_support_filter_program( a, QString( lzip + " -d" ).toLocal8Bit().data() ); - - if ( r < ARCHIVE_OK ) { - qDebug() << "Cannot use the input filter/format."; - return false; - } - - // Structure to write files to disk - ext = archive_write_disk_new(); - archive_write_disk_set_options( ext, flags ); - archive_write_disk_set_standard_lookup( ext ); - - if ( (r = archive_read_open_filename( a, archiveName.toLocal8Bit().data(), 10240 ) ) ) { - qDebug() << "Unable to read archive:" << archive_error_string( a ); - return false; - } - - while ( true ) { - r = archive_read_next_header( a, &entry ); - - if ( r == ARCHIVE_EOF ) { - qDebug() << "EOF"; - break; - } - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - } - - if ( r < ARCHIVE_WARN ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - return false; - } - - r = archive_write_header( ext, entry ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - else if ( archive_entry_size( entry ) == 0 ) { - r = copyData( a, ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return false; - } - } - - r = archive_write_finish_entry( ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return true; - } - } - - archive_read_close( a ); - archive_read_free( a ); - - archive_write_close( ext ); - archive_write_free( ext ); - - return true; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.uu" ) ) { - /* UUEncode Extractor */ - - return false; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lrz" ) ) { - /* lrzip Extractor */ - - QString lrzip; - -#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 ) - QStringList exeLocs = QString::fromLocal8Bit( qgetenv( "PATH" ) ).split( ":", Qt::SkipEmptyParts ); + Q_FOREACH (QString loc, exeLocs) + { + if (exists(loc + "/lzip")) + { + lzip = loc + "/lzip"; + break; + } + } + + if (not lzip.count()) + { + qDebug() << "External program lzip not found."; + return false; + } + + struct archive *a; + struct archive *ext; + struct archive_entry *entry; + int flags; + + int r = ARCHIVE_OK; + + /* Select which attributes we want to restore. */ + flags = ARCHIVE_EXTRACT_TIME; + flags |= ARCHIVE_EXTRACT_PERM; + flags |= ARCHIVE_EXTRACT_ACL; + flags |= ARCHIVE_EXTRACT_FFLAGS; + + // Source Archive + a = archive_read_new(); + r |= archive_read_support_format_raw(a); + r |= archive_read_support_filter_program(a, QString(lzip + " -d").toLocal8Bit().data()); + + if (r < ARCHIVE_OK) + { + qDebug() << "Cannot use the input filter/format."; + return false; + } + + // Structure to write files to disk + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + archive_write_disk_set_standard_lookup(ext); + + if ((r = archive_read_open_filename(a, archiveName.toLocal8Bit().data(), 10240))) + { + qDebug() << "Unable to read archive:" << archive_error_string(a); + return false; + } + + while (true) + { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + { + qDebug() << "EOF"; + break; + } + + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + } + + if (r < ARCHIVE_WARN) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + return false; + } + + r = archive_write_header(ext, entry); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + else if (archive_entry_size(entry) == 0) + { + r = copyData(a, ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return false; + } + } + + r = archive_write_finish_entry(ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return true; + } + } + + archive_read_close(a); + archive_read_free(a); + + archive_write_close(ext); + archive_write_free(ext); + + return true; + } + + else if (mType == mimeDb.mimeTypeForFile("file.uu")) + { + /* UUEncode Extractor */ + + return false; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lrz")) + { + /* lrzip Extractor */ + + QString lrzip; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QStringList exeLocs = QString::fromLocal8Bit(qgetenv("PATH")).split(":", Qt::SkipEmptyParts); #else - QStringList exeLocs = QString::fromLocal8Bit( qgetenv( "PATH" ) ).split( ":", QString::SkipEmptyParts ); + QStringList exeLocs = QString::fromLocal8Bit(qgetenv("PATH")).split(":", QString::SkipEmptyParts); #endif - Q_FOREACH (QString loc, exeLocs) { - if ( exists( loc + "/lrzip" ) ) { - lrzip = loc + "/lrzip"; - break; - } - } - - if ( not lrzip.length() ) { - qDebug() << "External program lrzip not found."; - return false; - } - - struct archive *a; - struct archive *ext; - struct archive_entry *entry; - int flags; - - int r = ARCHIVE_OK; - - /* Select which attributes we want to restore. */ - flags = ARCHIVE_EXTRACT_TIME; - flags |= ARCHIVE_EXTRACT_PERM; - flags |= ARCHIVE_EXTRACT_ACL; - flags |= ARCHIVE_EXTRACT_FFLAGS; - - // Source Archive - a = archive_read_new(); - r |= archive_read_support_format_raw( a ); - r |= archive_read_support_filter_program( a, QString( lrzip + " -d" ).toLocal8Bit().data() ); - - if ( r < ARCHIVE_OK ) { - qDebug() << "Cannot use the input filter/format."; - } - - // Structure to write files to disk - ext = archive_write_disk_new(); - archive_write_disk_set_options( ext, flags ); - archive_write_disk_set_standard_lookup( ext ); - - if ( (r = archive_read_open_filename( a, archiveName.toLocal8Bit().data(), 10240 ) ) ) { - qDebug() << "Unable to read archive:" << archive_error_string( a ); - return false; - } - - while ( true ) { - r = archive_read_next_header( a, &entry ); - - if ( r == ARCHIVE_EOF ) { - qDebug() << "EOF"; - break; - } - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - } - - if ( r < ARCHIVE_WARN ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - return false; - } - - r = archive_write_header( ext, entry ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - else if ( archive_entry_size( entry ) == 0 ) { - r = copyData( a, ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return false; - } - } - - r = archive_write_finish_entry( ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return true; - } - } - - archive_read_close( a ); - archive_read_free( a ); - - archive_write_close( ext ); - archive_write_free( ext ); - - return true; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lzo" ) ) { - /* LZop Extractor */ - - QString lzop; - -#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 ) - QStringList exeLocs = QString::fromLocal8Bit( qgetenv( "PATH" ) ).split( ":", Qt::SkipEmptyParts ); + Q_FOREACH (QString loc, exeLocs) + { + if (exists(loc + "/lrzip")) + { + lrzip = loc + "/lrzip"; + break; + } + } + + if (not lrzip.count()) + { + qDebug() << "External program lrzip not found."; + return false; + } + + struct archive *a; + struct archive *ext; + struct archive_entry *entry; + int flags; + + int r = ARCHIVE_OK; + + /* Select which attributes we want to restore. */ + flags = ARCHIVE_EXTRACT_TIME; + flags |= ARCHIVE_EXTRACT_PERM; + flags |= ARCHIVE_EXTRACT_ACL; + flags |= ARCHIVE_EXTRACT_FFLAGS; + + // Source Archive + a = archive_read_new(); + r |= archive_read_support_format_raw(a); + r |= archive_read_support_filter_program(a, QString(lrzip + " -d").toLocal8Bit().data()); + + if (r < ARCHIVE_OK) + { + qDebug() << "Cannot use the input filter/format."; + } + + // Structure to write files to disk + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + archive_write_disk_set_standard_lookup(ext); + + if ((r = archive_read_open_filename(a, archiveName.toLocal8Bit().data(), 10240))) + { + qDebug() << "Unable to read archive:" << archive_error_string(a); + return false; + } + + while (true) + { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + { + qDebug() << "EOF"; + break; + } + + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + } + + if (r < ARCHIVE_WARN) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + return false; + } + + r = archive_write_header(ext, entry); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + else if (archive_entry_size(entry) == 0) + { + r = copyData(a, ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return false; + } + } + + r = archive_write_finish_entry(ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return true; + } + } + + archive_read_close(a); + archive_read_free(a); + + archive_write_close(ext); + archive_write_free(ext); + + return true; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lzo")) + { + /* LZop Extractor */ + + QString lzop; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QStringList exeLocs = QString::fromLocal8Bit(qgetenv("PATH")).split(":", Qt::SkipEmptyParts); #else - QStringList exeLocs = QString::fromLocal8Bit( qgetenv( "PATH" ) ).split( ":", QString::SkipEmptyParts ); + QStringList exeLocs = QString::fromLocal8Bit(qgetenv("PATH")).split(":", QString::SkipEmptyParts); #endif - Q_FOREACH (QString loc, exeLocs) { - if ( exists( loc + "/lzop" ) ) { - lzop = loc + "/lzop"; - break; - } - } - - if ( not lzop.length() ) { - qDebug() << "External program lzop not found."; - return false; - } - - struct archive *a; - struct archive *ext; - struct archive_entry *entry; - int flags; - - int r = ARCHIVE_OK; - - /* Select which attributes we want to restore. */ - flags = ARCHIVE_EXTRACT_TIME; - flags |= ARCHIVE_EXTRACT_PERM; - flags |= ARCHIVE_EXTRACT_ACL; - flags |= ARCHIVE_EXTRACT_FFLAGS; - - // Source Archive - a = archive_read_new(); - r |= archive_read_support_format_raw( a ); - r |= archive_read_support_filter_program( a, QString( lzop + " -d" ).toLocal8Bit().data() ); - - if ( r < ARCHIVE_OK ) { - qDebug() << "Cannot use the input filter/format."; - } - - // Structure to write files to disk - ext = archive_write_disk_new(); - archive_write_disk_set_options( ext, flags ); - archive_write_disk_set_standard_lookup( ext ); - - if ( (r = archive_read_open_filename( a, archiveName.toLocal8Bit().data(), 10240 ) ) ) { - qDebug() << "Unable to read archive:" << archive_error_string( a ); - return false; - } - - while ( true ) { - r = archive_read_next_header( a, &entry ); - - if ( r == ARCHIVE_EOF ) { - qDebug() << "EOF"; - break; - } - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - } - - if ( r < ARCHIVE_WARN ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - return false; - } - - r = archive_write_header( ext, entry ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - else if ( archive_entry_size( entry ) == 0 ) { - r = copyData( a, ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return false; - } - } - - r = archive_write_finish_entry( ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return true; - } - } - - archive_read_close( a ); - archive_read_free( a ); - - archive_write_close( ext ); - archive_write_free( ext ); - - return true; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lz4" ) ) { - /* LZ4 Extractor */ - - dest = archiveName; - dest.chop( 4 ); - - int i = 0; - while ( exists( dest ) ) { - i++; - - dest = archiveName; - dest.chop( 3 ); - - dest = dirName( dest ) + QString( "(%1) - " ).arg( i ) + baseName( dest ); - } - - unlz4( archiveName.toLocal8Bit().constData(), dest.toLocal8Bit().constData(), NULL ); - return true; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.gz" ) ) { - /* GZip Extractor */ - - dest = archiveName; - dest.chop( 3 ); - - int i = 0; - while ( exists( dest ) ) { - i++; - - dest = archiveName; - dest.chop( 3 ); - - dest = dirName( dest ) + QString( "(%1) - " ).arg( i ) + baseName( dest ); - } - - NBGZip *gzExt = new NBGZip( archiveName, dest ); - return gzExt->extract(); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.bz2" ) ) { - /* BZip2 Extractor */ - - dest = archiveName; - dest.chop( 3 ); - - int i = 0; - while ( exists( dest ) ) { - i++; - - dest = archiveName; - dest.chop( 3 ); - - dest = dirName( dest ) + QString( "(%1) - " ).arg( i ) + baseName( dest ); - } - - NBBZip2 *bz2Ext = new NBBZip2( archiveName, dest ); - return bz2Ext->extract(); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lzma" ) ) { - /* LZMA Extractor */ - - dest = archiveName; - dest.chop( 3 ); - - int i = 0; - while ( exists( dest ) ) { - i++; - - dest = archiveName; - dest.chop( 3 ); - - dest = dirName( dest ) + QString( "(%1) - " ).arg( i ) + baseName( dest ); - } - - NBLzma *lzmaExt = new NBLzma( archiveName, dest ); - return lzmaExt->extract(); - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.xz" ) ) { - /* XZ Extractor */ - - dest = archiveName; - dest.chop( 3 ); - - int i = 0; - while ( exists( dest ) ) { - i++; - - dest = archiveName; - dest.chop( 3 ); - - dest = dirName( dest ) + QString( "(%1) - " ).arg( i ) + baseName( dest ); - } - - NBXz *xzExt = new NBXz( archiveName, dest ); - return xzExt->extract(); - } - - return false; - } - - else { - /* - * To show progress we want the number of entries. So list the archive first. - * Then count the number of members. Then clear the memberList - */ - listArchive(); - int entryCount = memberList.length(); - memberList.clear(); - readDone = false; - - struct archive *a; - struct archive *ext; - struct archive_entry *entry; - int flags; - int r = ARCHIVE_OK; - - int processedEntries = 0; - - /* Select which attributes we want to restore. */ - flags = ARCHIVE_EXTRACT_TIME; - flags |= ARCHIVE_EXTRACT_PERM; - flags |= ARCHIVE_EXTRACT_ACL; - flags |= ARCHIVE_EXTRACT_FFLAGS; - - // Source Archive - a = archive_read_new(); - - r |= archive_read_support_format_all( a ); - r |= archive_read_support_filter_all( a ); - - if ( (r |= archive_read_open_filename( a, archiveName.toUtf8().data(), 10240 ) ) < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - return false; - } - - r = ARCHIVE_OK; - - // Structure to write files to disk - ext = archive_write_disk_new(); - r |= archive_write_disk_set_options( ext, flags ); - r |= archive_write_disk_set_standard_lookup( ext ); - - if ( r < ARCHIVE_WARN ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - return false; - } - - while ( true ) { - r = archive_read_next_header( a, &entry ); - - if ( r == ARCHIVE_EOF ) { - break; - } - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - } - - if ( r < ARCHIVE_WARN ) { - return 1; - } - - r = archive_write_header( ext, entry ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - else if ( archive_entry_size( entry ) > 0 ) { - r = copyData( a, ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return 1; - } - } - - processedEntries++; - emit progress( processedEntries * 100 / entryCount ); - - qApp->processEvents(); - - r = archive_write_finish_entry( ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return 1; - } - } - - archive_read_close( a ); - archive_read_free( a ); - - archive_write_close( ext ); - archive_write_free( ext ); - - return true; - } - - chdir( srcDir ); + Q_FOREACH (QString loc, exeLocs) + { + if (exists(loc + "/lzop")) + { + lzop = loc + "/lzop"; + break; + } + } + + if (not lzop.count()) + { + qDebug() << "External program lzop not found."; + return false; + } + + struct archive *a; + struct archive *ext; + struct archive_entry *entry; + int flags; + + int r = ARCHIVE_OK; + + /* Select which attributes we want to restore. */ + flags = ARCHIVE_EXTRACT_TIME; + flags |= ARCHIVE_EXTRACT_PERM; + flags |= ARCHIVE_EXTRACT_ACL; + flags |= ARCHIVE_EXTRACT_FFLAGS; + + // Source Archive + a = archive_read_new(); + r |= archive_read_support_format_raw(a); + r |= archive_read_support_filter_program(a, QString(lzop + " -d").toLocal8Bit().data()); + + if (r < ARCHIVE_OK) + { + qDebug() << "Cannot use the input filter/format."; + } + + // Structure to write files to disk + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + archive_write_disk_set_standard_lookup(ext); + + if ((r = archive_read_open_filename(a, archiveName.toLocal8Bit().data(), 10240))) + { + qDebug() << "Unable to read archive:" << archive_error_string(a); + return false; + } + + while (true) + { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + { + qDebug() << "EOF"; + break; + } + + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + } + + if (r < ARCHIVE_WARN) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + return false; + } + + r = archive_write_header(ext, entry); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + else if (archive_entry_size(entry) == 0) + { + r = copyData(a, ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return false; + } + } + + r = archive_write_finish_entry(ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return true; + } + } + + archive_read_close(a); + archive_read_free(a); + + archive_write_close(ext); + archive_write_free(ext); + + return true; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lz4")) + { + /* LZ4 Extractor */ + + dest = archiveName; + dest.chop(4); + + int i = 0; + while (exists(dest)) + { + i++; + + dest = archiveName; + dest.chop(3); + + dest = dirName(dest) + QString("(%1) - ").arg(i) + baseName(dest); + } + + unlz4(archiveName.toLocal8Bit().constData(), dest.toLocal8Bit().constData(), NULL); + return true; + } + + else if (mType == mimeDb.mimeTypeForFile("file.gz")) + { + /* GZip Extractor */ + + dest = archiveName; + dest.chop(3); + + int i = 0; + while (exists(dest)) + { + i++; + + dest = archiveName; + dest.chop(3); + + dest = dirName(dest) + QString("(%1) - ").arg(i) + baseName(dest); + } + + NBGZip *gzExt = new NBGZip(archiveName, dest); + return gzExt->extract(); + } + + else if (mType == mimeDb.mimeTypeForFile("file.bz2")) + { + /* BZip2 Extractor */ + + dest = archiveName; + dest.chop(3); + + int i = 0; + while (exists(dest)) + { + i++; + + dest = archiveName; + dest.chop(3); + + dest = dirName(dest) + QString("(%1) - ").arg(i) + baseName(dest); + } + + NBBZip2 *bz2Ext = new NBBZip2(archiveName, dest); + return bz2Ext->extract(); + } + + else if (mType == mimeDb.mimeTypeForFile("file.lzma")) + { + /* LZMA Extractor */ + + dest = archiveName; + dest.chop(3); + + int i = 0; + while (exists(dest)) + { + i++; + + dest = archiveName; + dest.chop(3); + + dest = dirName(dest) + QString("(%1) - ").arg(i) + baseName(dest); + } + + NBLzma *lzmaExt = new NBLzma(archiveName, dest); + return lzmaExt->extract(); + } + + else if (mType == mimeDb.mimeTypeForFile("file.xz")) + { + /* XZ Extractor */ + + dest = archiveName; + dest.chop(3); + + int i = 0; + while (exists(dest)) + { + i++; + + dest = archiveName; + dest.chop(3); + + dest = dirName(dest) + QString("(%1) - ").arg(i) + baseName(dest); + } + + NBXz *xzExt = new NBXz(archiveName, dest); + return xzExt->extract(); + } + + return false; + } + + else + { + /* To show progress we want the number of entries. So list the archive first. */ + /* Then count the number of members. Then clear the memberList */ + listArchive(); + int entryCount = memberList.count(); + memberList.clear(); + readDone = false; + + struct archive *a; + struct archive *ext; + struct archive_entry *entry; + int flags; + int r = ARCHIVE_OK; + + int processedEntries = 0; + + /* Select which attributes we want to restore. */ + flags = ARCHIVE_EXTRACT_TIME; + flags |= ARCHIVE_EXTRACT_PERM; + flags |= ARCHIVE_EXTRACT_ACL; + flags |= ARCHIVE_EXTRACT_FFLAGS; + + // Source Archive + a = archive_read_new(); + + r |= archive_read_support_format_all(a); + r |= archive_read_support_filter_all(a); + + if ((r |= archive_read_open_filename(a, archiveName.toUtf8().data(), 10240)) < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + return false; + } + + r = ARCHIVE_OK; + + // Structure to write files to disk + ext = archive_write_disk_new(); + r |= archive_write_disk_set_options(ext, flags); + r |= archive_write_disk_set_standard_lookup(ext); + + if (r < ARCHIVE_WARN) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + return false; + } + + while (true) + { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + { + break; + } + + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + } + + if (r < ARCHIVE_WARN) + { + return 1; + } + + r = archive_write_header(ext, entry); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + else if (archive_entry_size(entry) > 0) + { + r = copyData(a, ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return 1; + } + } + + processedEntries++; + emit progress(processedEntries * 100 / entryCount); + + qApp->processEvents(); + + r = archive_write_finish_entry(ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return 1; + } + } + + archive_read_close(a); + archive_read_free(a); + + archive_write_close(ext); + archive_write_free(ext); + + return true; + } + + chdir(srcDir); } -bool LibArchiveQt::doExtractMember( QString memberName ) { - listArchive(); - - if ( archiveType == Single ) { - return doExtractMember( memberName ); - } - - // Change to the target directory - char srcDir[ 10240 ] = { 0 }; - - getcwd( srcDir, 10240 ); - chdir( dest.toUtf8().data() ); - - struct archive *a; - struct archive *ext; - struct archive_entry *entry; - int flags; - int r; - - /* Select which attributes we want to restore. */ - flags = ARCHIVE_EXTRACT_TIME; - flags |= ARCHIVE_EXTRACT_PERM; - flags |= ARCHIVE_EXTRACT_ACL; - flags |= ARCHIVE_EXTRACT_FFLAGS; - - // Source Archive - a = archive_read_new(); - archive_read_support_format_all( a ); - archive_read_support_filter_all( a ); - - // Structure to write files to disk - ext = archive_write_disk_new(); - archive_write_disk_set_options( ext, flags ); - archive_write_disk_set_standard_lookup( ext ); - - r = archive_read_open_filename( a, archiveName.toUtf8().data(), 10240 ); - - if ( r != ARCHIVE_OK ) { - qDebug() << "[ERROR]: Failed to open archive:" << archiveName; - return true; - } - - bool dir = false, found = false; - - /* Direct member */ - Q_FOREACH (ArchiveEntry *ae, memberList) { - if ( ae->name == memberName ) { - dir = (ae->type == AE_IFDIR); - found = true; - break; - } - - if ( ae->name == memberName + "/" ) { - memberName += "/"; - dir = (ae->type == AE_IFDIR); - found = true; - break; - } - } - - if ( not found ) { - /* Always check for @memberName + "/" because, all indirect members will be directories */ - memberName += "/"; - - /* Indirect member: ex. debug/ is a member if debug/path/to/file.ext exists */ - Q_FOREACH (ArchiveEntry *ae, memberList) { - if ( ae->name.startsWith( memberName ) == 0 ) { - dir = true; - found = true; - break; - } - } - } - - if ( found ) { - while ( true ) { - r = archive_read_next_header( a, &entry ); - - if ( r == ARCHIVE_EOF ) { - break; - } - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( a ) ); - } - - if ( r < ARCHIVE_WARN ) { - return true; - } - - QString entryPath = archive_entry_pathname( entry ); - - /* Check if the current entry starts with @memberName */ - if ( entryPath.startsWith( memberName ) ) { - if ( not dir ) { - if ( entryPath != memberName ) { - continue; - } - } - - r = archive_write_header( ext, entry ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - else if ( archive_entry_size( entry ) > 0 ) { - r = copyData( a, ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return false; - } - } - - r = archive_write_finish_entry( ext ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( ext ) ); - } - - if ( r < ARCHIVE_WARN ) { - return false; - } - } - } - } - - else { - qDebug() << "[Error]" << "File not found in the archive:" << memberName; - return false; - } - - archive_read_close( a ); - archive_read_free( a ); - - archive_write_close( ext ); - archive_write_free( ext ); - - chdir( srcDir ); - - return true; +bool LibArchiveQt::doExtractMember(QString memberName) +{ + listArchive(); + + if (archiveType == Single) + { + return doExtractMember(memberName); + } + + // Change to the target directory + char srcDir[10240] = { 0 }; + + getcwd(srcDir, 10240); + chdir(dest.toUtf8().data()); + + struct archive *a; + struct archive *ext; + struct archive_entry *entry; + int flags; + int r; + + /* Select which attributes we want to restore. */ + flags = ARCHIVE_EXTRACT_TIME; + flags |= ARCHIVE_EXTRACT_PERM; + flags |= ARCHIVE_EXTRACT_ACL; + flags |= ARCHIVE_EXTRACT_FFLAGS; + + // Source Archive + a = archive_read_new(); + archive_read_support_format_all(a); + archive_read_support_filter_all(a); + + // Structure to write files to disk + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + archive_write_disk_set_standard_lookup(ext); + + r = archive_read_open_filename(a, archiveName.toUtf8().data(), 10240); + if (r != ARCHIVE_OK) + { + qDebug() << "[ERROR]: Failed to open archive:" << archiveName; + return true; + } + + bool dir = false, found = false; + + /* Direct member */ + Q_FOREACH (ArchiveEntry *ae, memberList) + { + if (ae->name == memberName) + { + dir = (ae->type == AE_IFDIR); + found = true; + break; + } + + if (ae->name == memberName + "/") + { + memberName += "/"; + dir = (ae->type == AE_IFDIR); + found = true; + break; + } + } + + if (not found) + { + /* Always check for @memberName + "/" because, all indirect members will be directories */ + memberName += "/"; + + /* Indirect member: ex. debug/ is a member if debug/path/to/file.ext exists */ + Q_FOREACH (ArchiveEntry *ae, memberList) + { + if (ae->name.startsWith(memberName) == 0) + { + dir = true; + found = true; + break; + } + } + } + + if (found) + { + while (true) + { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + { + break; + } + + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(a)); + } + + if (r < ARCHIVE_WARN) + { + return true; + } + + QString entryPath = archive_entry_pathname(entry); + + /* Check if the current entry starts with @memberName */ + if (entryPath.startsWith(memberName)) + { + if (not dir) + { + if (entryPath != memberName) + { + continue; + } + } + + r = archive_write_header(ext, entry); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + else if (archive_entry_size(entry) > 0) + { + r = copyData(a, ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return false; + } + } + + r = archive_write_finish_entry(ext); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(ext)); + } + + if (r < ARCHIVE_WARN) + { + return false; + } + } + } + } + + else + { + qDebug() << "[Error]" << "File not found in the archive:" << memberName; + return false; + } + + archive_read_close(a); + archive_read_free(a); + + archive_write_close(ext); + archive_write_free(ext); + + chdir(srcDir); + + return true; } -int LibArchiveQt::copyData( struct archive *ar, struct archive *aw ) { - int r; - const void *buff; - size_t size; +int LibArchiveQt::copyData(struct archive *ar, struct archive *aw) +{ + int r; + const void *buff; + size_t size; #ifdef __LP64__ - off_t offset; + off_t offset; #else - la_int64_t offset; + la_int64_t offset; #endif - while ( true ) { - r = archive_read_data_block( ar, &buff, &size, &offset ); - - if ( r == ARCHIVE_EOF ) { - return (ARCHIVE_OK); - } - - if ( r < ARCHIVE_OK ) { - return (r); - } - - r = archive_write_data_block( aw, buff, size, offset ); - - if ( r < ARCHIVE_OK ) { - fprintf( stderr, "%s\n", archive_error_string( aw ) ); - return (r); - } - } + while (true) + { + r = archive_read_data_block(ar, &buff, &size, &offset); + if (r == ARCHIVE_EOF) + { + return(ARCHIVE_OK); + } + + if (r < ARCHIVE_OK) + { + return(r); + } + + r = archive_write_data_block(aw, buff, size, offset); + if (r < ARCHIVE_OK) + { + fprintf(stderr, "%s\n", archive_error_string(aw)); + return(r); + } + } } -void LibArchiveQt::setFilterFormat( QMimeType mType ) { - if ( mType == mimeDb.mimeTypeForFile( "file.cpio" ) ) { - mArchiveFilter = ARCHIVE_FILTER_NONE; - mArchiveFormat = ARCHIVE_FORMAT_CPIO; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.shar" ) ) { - mArchiveFilter = ARCHIVE_FILTER_NONE; - mArchiveFormat = ARCHIVE_FORMAT_SHAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar" ) ) { - mArchiveFilter = ARCHIVE_FILTER_NONE; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.gz" ) ) { - mArchiveFilter = ARCHIVE_FILTER_GZIP; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.grz" ) ) { - mArchiveFilter = ARCHIVE_FILTER_GRZIP; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.xz" ) ) { - mArchiveFilter = ARCHIVE_FILTER_XZ; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lzo" ) ) { - mArchiveFilter = ARCHIVE_FILTER_LZOP; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lzma" ) ) { - mArchiveFilter = ARCHIVE_FILTER_LZMA; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lz" ) ) { - mArchiveFilter = ARCHIVE_FILTER_LZIP; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lrz" ) ) { - mArchiveFilter = ARCHIVE_FILTER_LRZIP; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } +void LibArchiveQt::setFilterFormat(QMimeType mType) +{ + if (mType == mimeDb.mimeTypeForFile("file.cpio")) + { + mArchiveFilter = ARCHIVE_FILTER_NONE; + mArchiveFormat = ARCHIVE_FORMAT_CPIO; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.shar")) + { + mArchiveFilter = ARCHIVE_FILTER_NONE; + mArchiveFormat = ARCHIVE_FORMAT_SHAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar")) + { + mArchiveFilter = ARCHIVE_FILTER_NONE; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.gz")) + { + mArchiveFilter = ARCHIVE_FILTER_GZIP; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.grz")) + { + mArchiveFilter = ARCHIVE_FILTER_GRZIP; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.xz")) + { + mArchiveFilter = ARCHIVE_FILTER_XZ; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.lzo")) + { + mArchiveFilter = ARCHIVE_FILTER_LZOP; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.lzma")) + { + mArchiveFilter = ARCHIVE_FILTER_LZMA; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.lz")) + { + mArchiveFilter = ARCHIVE_FILTER_LZIP; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.lrz")) + { + mArchiveFilter = ARCHIVE_FILTER_LRZIP; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } #if ARCHIVE_VERSION_NUMBER > 3001002 - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.lz4" ) ) { - mArchiveFilter = ARCHIVE_FILTER_LZ4; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } + else if (mType == mimeDb.mimeTypeForFile("file.tar.lz4")) + { + mArchiveFilter = ARCHIVE_FILTER_LZ4; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } #endif - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.bz2" ) ) { - mArchiveFilter = ARCHIVE_FILTER_BZIP2; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.tar.Z" ) ) { - mArchiveFilter = ARCHIVE_FILTER_COMPRESS; - mArchiveFormat = ARCHIVE_FORMAT_TAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.iso" ) ) { - mArchiveFilter = ARCHIVE_FILTER_NONE; - mArchiveFormat = ARCHIVE_FORMAT_ISO9660; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.zip" ) ) { - mArchiveFilter = ARCHIVE_FILTER_NONE; - mArchiveFormat = ARCHIVE_FORMAT_ZIP; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.ar" ) ) { - mArchiveFilter = ARCHIVE_FILTER_NONE; - mArchiveFormat = ARCHIVE_FORMAT_AR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.xar" ) ) { - mArchiveFilter = ARCHIVE_FILTER_NONE; - mArchiveFormat = ARCHIVE_FORMAT_XAR; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.7z" ) ) { - mArchiveFilter = ARCHIVE_FILTER_NONE; - mArchiveFormat = ARCHIVE_FORMAT_7ZIP; - archiveType = Container; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lz" ) ) { - mArchiveFilter = ARCHIVE_FILTER_LZIP; - mArchiveFormat = ARCHIVE_FORMAT_RAW; - archiveType = Single; - } + else if (mType == mimeDb.mimeTypeForFile("file.tar.bz2")) + { + mArchiveFilter = ARCHIVE_FILTER_BZIP2; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.tar.Z")) + { + mArchiveFilter = ARCHIVE_FILTER_COMPRESS; + mArchiveFormat = ARCHIVE_FORMAT_TAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.iso")) + { + mArchiveFilter = ARCHIVE_FILTER_NONE; + mArchiveFormat = ARCHIVE_FORMAT_ISO9660; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.zip")) + { + mArchiveFilter = ARCHIVE_FILTER_NONE; + mArchiveFormat = ARCHIVE_FORMAT_ZIP; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.ar")) + { + mArchiveFilter = ARCHIVE_FILTER_NONE; + mArchiveFormat = ARCHIVE_FORMAT_AR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.xar")) + { + mArchiveFilter = ARCHIVE_FILTER_NONE; + mArchiveFormat = ARCHIVE_FORMAT_XAR; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.7z")) + { + mArchiveFilter = ARCHIVE_FILTER_NONE; + mArchiveFormat = ARCHIVE_FORMAT_7ZIP; + archiveType = Container; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lz")) + { + mArchiveFilter = ARCHIVE_FILTER_LZIP; + mArchiveFormat = ARCHIVE_FORMAT_RAW; + archiveType = Single; + } #if ARCHIVE_VERSION_NUMBER > 3001002 - else if ( mType == mimeDb.mimeTypeForFile( "file.lz4" ) ) { - mArchiveFilter = ARCHIVE_FILTER_LZ4; - mArchiveFormat = ARCHIVE_FORMAT_RAW; - archiveType = Single; - } + else if (mType == mimeDb.mimeTypeForFile("file.lz4")) + { + mArchiveFilter = ARCHIVE_FILTER_LZ4; + mArchiveFormat = ARCHIVE_FORMAT_RAW; + archiveType = Single; + } #endif - else if ( mType == mimeDb.mimeTypeForFile( "file.uu" ) ) { - mArchiveFilter = ARCHIVE_FILTER_UU; - mArchiveFormat = ARCHIVE_FORMAT_RAW; - archiveType = Single; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lzo" ) ) { - mArchiveFilter = ARCHIVE_FILTER_LZOP; - mArchiveFormat = ARCHIVE_FORMAT_RAW; - archiveType = Single; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.gz" ) ) { - mArchiveFilter = ARCHIVE_FILTER_GZIP; - mArchiveFormat = ARCHIVE_FORMAT_RAW; - archiveType = Single; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.bz2" ) ) { - mArchiveFilter = ARCHIVE_FILTER_BZIP2; - mArchiveFormat = ARCHIVE_FORMAT_RAW; - archiveType = Single; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.lzma" ) ) { - mArchiveFilter = ARCHIVE_FILTER_LZMA; - mArchiveFormat = ARCHIVE_FORMAT_RAW; - archiveType = Single; - } - - else if ( mType == mimeDb.mimeTypeForFile( "file.xz" ) ) { - mArchiveFilter = ARCHIVE_FILTER_XZ; - mArchiveFormat = ARCHIVE_FORMAT_RAW; - archiveType = Single; - } - - else { - mArchiveFormat = ARCHIVE_FORMAT_EMPTY; - mArchiveFilter = ARCHIVE_FILTER_NONE; - archiveType = None; - } + else if (mType == mimeDb.mimeTypeForFile("file.uu")) + { + mArchiveFilter = ARCHIVE_FILTER_UU; + mArchiveFormat = ARCHIVE_FORMAT_RAW; + archiveType = Single; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lzo")) + { + mArchiveFilter = ARCHIVE_FILTER_LZOP; + mArchiveFormat = ARCHIVE_FORMAT_RAW; + archiveType = Single; + } + + else if (mType == mimeDb.mimeTypeForFile("file.gz")) + { + mArchiveFilter = ARCHIVE_FILTER_GZIP; + mArchiveFormat = ARCHIVE_FORMAT_RAW; + archiveType = Single; + } + + else if (mType == mimeDb.mimeTypeForFile("file.bz2")) + { + mArchiveFilter = ARCHIVE_FILTER_BZIP2; + mArchiveFormat = ARCHIVE_FORMAT_RAW; + archiveType = Single; + } + + else if (mType == mimeDb.mimeTypeForFile("file.lzma")) + { + mArchiveFilter = ARCHIVE_FILTER_LZMA; + mArchiveFormat = ARCHIVE_FORMAT_RAW; + archiveType = Single; + } + + else if (mType == mimeDb.mimeTypeForFile("file.xz")) + { + mArchiveFilter = ARCHIVE_FILTER_XZ; + mArchiveFormat = ARCHIVE_FORMAT_RAW; + archiveType = Single; + } + + else + { + mArchiveFormat = ARCHIVE_FORMAT_EMPTY; + mArchiveFilter = ARCHIVE_FILTER_NONE; + archiveType = None; + } } diff --git lib/LibBZip2.cpp lib/LibBZip2.cpp index 49e4b60..206cfa1 100644 --- lib/LibBZip2.cpp +++ lib/LibBZip2.cpp @@ -1,19 +1,27 @@ -/** - * Copyright 2018 Britanicus - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - **/ +/* + * + * Copyright 2018 Britanicus + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ // Local Headers #include @@ -23,69 +31,80 @@ const int MAX_READ_SIZE = 40960; QString NBBZip2::bz2FileName = QString(); QString NBBZip2::fileName = QString(); -NBBZip2::NBBZip2( QString archive, QString file ) { - int error = 0; - - bz2FileName = QString( archive ); - - if ( not file.isEmpty() ) { - if ( QFileInfo( file ).isDir() ) { - fileName = QDir( file ).filePath( QString( archive ) ); - fileName.chop( 4 ); - } - - else if ( QFileInfo( file ).exists() ) { - QFile::rename( file, file + ".old" ); - fileName = QString( file ); - } - - else { - fileName = QString( file ); - } - } - - else { - fileName = QString( archive ); - fileName.chop( 4 ); - } - - bzFile = fopen( qPrintable( bz2FileName ), "r" ); - bz2 = BZ2_bzReadOpen( &error, bzFile, 0, 0, NULL, 0 ); +NBBZip2::NBBZip2(QString archive, QString file) +{ + int error = 0; + + bz2FileName = QString(archive); + if (not file.isEmpty()) + { + if (QFileInfo(file).isDir()) + { + fileName = QDir(file).filePath(QString(archive)); + fileName.chop(4); + } + + else if (QFileInfo(file).exists()) + { + QFile::rename(file, file + ".old"); + fileName = QString(file); + } + + else + { + fileName = QString(file); + } + } + + else + { + fileName = QString(archive); + fileName.chop(4); + } + + bzFile = fopen(qPrintable(bz2FileName), "r"); + bz2 = BZ2_bzReadOpen(&error, bzFile, 0, 0, NULL, 0); } -bool NBBZip2::extract() { - int error; +bool NBBZip2::extract() +{ + int error; - // Reading from the bz2 file opened - std::ofstream ofile( qPrintable( fileName ), std::ofstream::binary ); + // Reading from the bz2 file opened + std::ofstream ofile(qPrintable(fileName), std::ofstream::binary); - while ( true ) { - char buffer[ MAX_READ_SIZE ] = { "\x00" }; - int charsRead = BZ2_bzRead( &error, bz2, buffer, MAX_READ_SIZE ); - ofile.write( buffer, charsRead ); + while (true) + { + char buffer[MAX_READ_SIZE] = { "\x00" }; + int charsRead = BZ2_bzRead(&error, bz2, buffer, MAX_READ_SIZE); + ofile.write(buffer, charsRead); - if ( error == BZ_OK ) { - continue; - } + if (error == BZ_OK) + { + continue; + } - else { - break; - } - } + else + { + break; + } + } - if ( error != BZ_STREAM_END ) { - return false; - } + if (error != BZ_STREAM_END) + { + return false; + } - // Close the file - BZ2_bzReadClose( &error, bz2 ); + // Close the file + BZ2_bzReadClose(&error, bz2); - if ( error != BZ_OK ) { - return false; - } + if (error != BZ_OK) + { + return false; + } - fclose( bzFile ); + fclose(bzFile); - return true; + return true; } diff --git lib/LibBZip2.hpp lib/LibBZip2.hpp index 1403337..c3660a4 100644 --- lib/LibBZip2.hpp +++ lib/LibBZip2.hpp @@ -1,6 +1,8 @@ -/** - * LibBZip2.hpp - LibBZip2.cpp header - **/ +/* + * + * LibBZip2.hpp - LibBZip2.cpp header + * +*/ #pragma once @@ -9,15 +11,16 @@ #include class NBBZip2 { - public: - NBBZip2( QString, QString file = QString() ); - bool extract(); - static QString fileName; - static QString bz2FileName; + public: + NBBZip2( QString, QString file = QString() ); + bool extract(); - private: - BZFILE *bz2; - FILE *bzFile; - int NBBZip2Error; + static QString fileName; + static QString bz2FileName; + + private: + BZFILE *bz2; + FILE *bzFile; + int NBBZip2Error; }; diff --git lib/LibGZip.cpp lib/LibGZip.cpp index b2d5d82..b36f8b5 100644 --- lib/LibGZip.cpp +++ lib/LibGZip.cpp @@ -1,82 +1,100 @@ -/** - * Copyright 2018 Britanicus - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - **/ +/* + * + * Copyright 2018 Britanicus + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ // Local Headers -#include "LibGZip.hpp" +#include const int MAX_READ_SIZE = 40960; QString NBGZip::gzFileName = QString(); QString NBGZip::fileName = QString(); -NBGZip::NBGZip( QString archive, QString file ) { - gzFileName = QString( archive ); - - if ( not file.isEmpty() ) { - if ( QFileInfo( file ).isDir() ) { - fileName = QDir( file ).filePath( QString( archive ) ); - fileName.chop( 3 ); - } - - else if ( QFileInfo( file ).exists() ) { - QFile::rename( file, file + ".old" ); - fileName = QString( file ); - } - - else { - fileName = QString( file ); - } - } - - gzip = gzopen( qPrintable( gzFileName ), "rb" ); +NBGZip::NBGZip(QString archive, QString file) +{ + gzFileName = QString(archive); + if (not file.isEmpty()) + { + if (QFileInfo(file).isDir()) + { + fileName = QDir(file).filePath(QString(archive)); + fileName.chop(3); + } + + else if (QFileInfo(file).exists()) + { + QFile::rename(file, file + ".old"); + fileName = QString(file); + } + + else + { + fileName = QString(file); + } + } + + gzip = gzopen(qPrintable(gzFileName), "rb"); } -bool NBGZip::extract() { - if ( gzip == NULL ) { - return true; - } - - // Reading from the bz2 file opened - std::ofstream ofile( qPrintable( fileName ), std::ofstream::binary ); - - while ( true ) { - char buffer[ MAX_READ_SIZE ] = { "\x00" }; - int charsRead = gzread( gzip, buffer, sizeof(buffer) ); - - if ( charsRead > 0 ) { - ofile.write( buffer, charsRead ); - } - - else if ( charsRead == 0 ) { - break; - } - - else { - return true; - } - } - - // Close the file - ofile.close(); - - if ( gzclose( gzip ) != Z_OK ) { - return true; - } - - return true; +bool NBGZip::extract() +{ + if (gzip == NULL) + { + return true; + } + + // Reading from the bz2 file opened + std::ofstream ofile(qPrintable(fileName), std::ofstream::binary); + + while (true) + { + char buffer[MAX_READ_SIZE] = { "\x00" }; + int charsRead = gzread(gzip, buffer, sizeof(buffer)); + + if (charsRead > 0) + { + ofile.write(buffer, charsRead); + } + + else if (charsRead == 0) + { + break; + } + + else + { + return true; + } + } + + // Close the file + ofile.close(); + if (gzclose(gzip) != Z_OK) + { + return true; + } + + return true; } diff --git lib/LibGZip.hpp lib/LibGZip.hpp index 3914b3b..dd836d1 100644 --- lib/LibGZip.hpp +++ lib/LibGZip.hpp @@ -1,6 +1,8 @@ -/** - * LibGZip.hpp - LibGZip.cpp header - **/ +/* + * + * LibGZip.hpp - LibGZip.cpp header + * +*/ #pragma once @@ -9,14 +11,15 @@ #include class NBGZip { - public: - NBGZip( QString, QString file = QString() ); - bool extract(); - static QString fileName; - static QString gzFileName; + public: + NBGZip( QString, QString file = QString() ); + bool extract(); - private: - gzFile gzip; - int NBGZipError; + static QString fileName; + static QString gzFileName; + + private: + gzFile gzip; + int NBGZipError; }; diff --git lib/LibLzma.cpp lib/LibLzma.cpp index 55222c0..b0fafd2 100644 --- lib/LibLzma.cpp +++ lib/LibLzma.cpp @@ -1,19 +1,27 @@ -/** - * Copyright 2018 Britanicus - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - **/ +/* + * + * Copyright 2018 Britanicus + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ // Local Headers #include @@ -23,87 +31,101 @@ const int MAX_READ_SIZE = 40960; QString NBLzma::lzmaFileName = QString(); QString NBLzma::fileName = QString(); -NBLzma::NBLzma( QString archive, QString file ) { - lzmaFileName = QString( archive ); - - if ( not file.isEmpty() ) { - if ( QFileInfo( file ).isDir() ) { - fileName = QDir( file ).filePath( QString( archive ) ); - fileName.chop( 3 ); - } - - else if ( QFileInfo( file ).exists() ) { - QFile::rename( file, file + ".old" ); - fileName = QString( file ); - } - - else { - fileName = QString( file ); - } - } - - fdin = fopen( qPrintable( lzmaFileName ), "rb" ); - fdout = fopen( qPrintable( fileName ), "wb" ); +NBLzma::NBLzma(QString archive, QString file) +{ + lzmaFileName = QString(archive); + if (not file.isEmpty()) + { + if (QFileInfo(file).isDir()) + { + fileName = QDir(file).filePath(QString(archive)); + fileName.chop(3); + } + + else if (QFileInfo(file).exists()) + { + QFile::rename(file, file + ".old"); + fileName = QString(file); + } + + else + { + fileName = QString(file); + } + } + + fdin = fopen(qPrintable(lzmaFileName), "rb"); + fdout = fopen(qPrintable(fileName), "wb"); } -bool NBLzma::extract() { - lzma_stream strm = LZMA_STREAM_INIT; - lzma_ret ret; - - // Initialize the decoder - ret = lzma_alone_decoder( &strm, UINT64_MAX ); - - if ( ret != LZMA_OK ) { - return false; - } - - uint8_t in_buf[ MAX_READ_SIZE ]; - uint8_t out_buf[ MAX_READ_SIZE ]; - - strm.avail_in = 0; - strm.next_out = out_buf; - strm.avail_out = MAX_READ_SIZE; - - lzma_action action = LZMA_RUN; - - while ( true ) { - if ( strm.avail_in == 0 ) { - strm.next_in = in_buf; - strm.avail_in = fread( in_buf, 1, MAX_READ_SIZE, fdin ); - - if ( ferror( fdin ) ) { - return false; - } - } - - if ( feof( fdin ) ) { - action = LZMA_FINISH; - } - - ret = lzma_code( &strm, action ); - - if ( (strm.avail_out == 0) || (ret != LZMA_OK) ) { - const size_t write_size = MAX_READ_SIZE - strm.avail_out; - - if ( fwrite( out_buf, 1, write_size, fdout ) != write_size ) { - return false; - } - - strm.next_out = out_buf; - strm.avail_out = MAX_READ_SIZE; - } - - if ( ret != LZMA_OK ) { - if ( ret == LZMA_STREAM_END ) { - // lzma_stream_decoder() already guarantees that there's no trailing garbage. - assert( strm.avail_in == 0 ); - assert( action == LZMA_FINISH ); - assert( feof( fdin ) ); - return false; - } - } - } - - return false; +bool NBLzma::extract() +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_ret ret; + + // Initialize the decoder + ret = lzma_alone_decoder(&strm, UINT64_MAX); + + if (ret != LZMA_OK) + { + return false; + } + + uint8_t in_buf[MAX_READ_SIZE]; + uint8_t out_buf[MAX_READ_SIZE]; + + strm.avail_in = 0; + strm.next_out = out_buf; + strm.avail_out = MAX_READ_SIZE; + + lzma_action action = LZMA_RUN; + + while (true) + { + if (strm.avail_in == 0) + { + strm.next_in = in_buf; + strm.avail_in = fread(in_buf, 1, MAX_READ_SIZE, fdin); + + if (ferror(fdin)) + { + return false; + } + } + + if (feof(fdin)) + { + action = LZMA_FINISH; + } + + ret = lzma_code(&strm, action); + + if ((strm.avail_out == 0) || (ret != LZMA_OK)) + { + const size_t write_size = MAX_READ_SIZE - strm.avail_out; + + if (fwrite(out_buf, 1, write_size, fdout) != write_size) + { + return false; + } + + strm.next_out = out_buf; + strm.avail_out = MAX_READ_SIZE; + } + + if (ret != LZMA_OK) + { + if (ret == LZMA_STREAM_END) + { + // lzma_stream_decoder() already guarantees that there's no trailing garbage. + assert(strm.avail_in == 0); + assert(action == LZMA_FINISH); + assert(feof(fdin)); + return false; + } + } + } + + return false; } diff --git lib/LibLzma.hpp lib/LibLzma.hpp index 3a4d347..02e4586 100644 --- lib/LibLzma.hpp +++ lib/LibLzma.hpp @@ -1,6 +1,8 @@ -/** - * LibLzma.hpp - LibLzma.cpp header - **/ +/* + * + * LibLzma.hpp - LibLzma.cpp header + * +*/ #pragma once @@ -9,14 +11,15 @@ #include class NBLzma { - public: - NBLzma( QString, QString file = QString() ); - bool extract(); - static QString fileName; - static QString lzmaFileName; + public: + NBLzma( QString, QString file = QString() ); + bool extract(); - private: - FILE *fdin, *fdout; - int NBLzmaError; + static QString fileName; + static QString lzmaFileName; + + private: + FILE *fdin, *fdout; + int NBLzmaError; }; diff --git lib/LibLzma2.cpp lib/LibLzma2.cpp index 109d2bf..7fb7d5e 100644 --- lib/LibLzma2.cpp +++ lib/LibLzma2.cpp @@ -1,19 +1,27 @@ -/** - * Copyright 2018 Britanicus - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - **/ +/* + * + * Copyright 2018 Britanicus + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ // Local Headers #include @@ -23,92 +31,108 @@ const int MAX_READ_SIZE = 40960; QString NBXz::xzFileName = QString(); QString NBXz::fileName = QString(); -NBXz::NBXz( QString archive, QString file ) { - xzFileName = QString( archive ); - - if ( not file.isEmpty() ) { - if ( QFileInfo( file ).isDir() ) { - fileName = QDir( file ).filePath( QString( archive ) ); - fileName.chop( 3 ); - } - - else if ( QFileInfo( file ).exists() ) { - QFile::rename( file, file + ".old" ); - fileName = QString( file ); - } - - else { - fileName = QString( file ); - } - } - - else { - fileName = file; - fileName.chop( 3 ); - } - - fdin = fopen( qPrintable( xzFileName ), "rb" ); - fdout = fopen( qPrintable( fileName ), "wb" ); +NBXz::NBXz(QString archive, QString file) +{ + xzFileName = QString(archive); + + if (not file.isEmpty()) + { + if (QFileInfo(file).isDir()) + { + fileName = QDir(file).filePath(QString(archive)); + fileName.chop(3); + } + + else if (QFileInfo(file).exists()) + { + QFile::rename(file, file + ".old"); + fileName = QString(file); + } + + else + { + fileName = QString(file); + } + } + + else + { + fileName = file; + fileName.chop(3); + } + + fdin = fopen(qPrintable(xzFileName), "rb"); + fdout = fopen(qPrintable(fileName), "wb"); } -bool NBXz::extract() { - lzma_stream strm = LZMA_STREAM_INIT; - lzma_ret ret; - - // Initialize the decoder - ret = lzma_stream_decoder( &strm, UINT64_MAX, LZMA_CONCATENATED ); - - if ( ret != LZMA_OK ) { - return false; - } - - uint8_t in_buf[ MAX_READ_SIZE ]; - uint8_t out_buf[ MAX_READ_SIZE ]; - - strm.avail_in = 0; - strm.next_out = out_buf; - strm.avail_out = MAX_READ_SIZE; - - lzma_action action = LZMA_RUN; - - while ( true ) { - if ( strm.avail_in == 0 ) { - strm.next_in = in_buf; - strm.avail_in = fread( in_buf, 1, MAX_READ_SIZE, fdin ); - - if ( ferror( fdin ) ) { - return false; - } - } - - if ( feof( fdin ) ) { - action = LZMA_FINISH; - } - - ret = lzma_code( &strm, action ); - - if ( (strm.avail_out == 0) || (ret != LZMA_OK) ) { - const size_t write_size = MAX_READ_SIZE - strm.avail_out; - - if ( fwrite( out_buf, 1, write_size, fdout ) != write_size ) { - return false; - } - - strm.next_out = out_buf; - strm.avail_out = MAX_READ_SIZE; - } - - if ( ret != LZMA_OK ) { - if ( ret == LZMA_STREAM_END ) { - // lzma_stream_decoder() already guarantees that there's no trailing garbage. - assert( strm.avail_in == 0 ); - assert( action == LZMA_FINISH ); - assert( feof( fdin ) ); - return false; - } - } - } - - return true; +bool NBXz::extract() +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_ret ret; + + // Initialize the decoder + ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED); + + if (ret != LZMA_OK) + { + return false; + } + + uint8_t in_buf[MAX_READ_SIZE]; + uint8_t out_buf[MAX_READ_SIZE]; + + strm.avail_in = 0; + strm.next_out = out_buf; + strm.avail_out = MAX_READ_SIZE; + + lzma_action action = LZMA_RUN; + + while (true) + { + if (strm.avail_in == 0) + { + strm.next_in = in_buf; + strm.avail_in = fread(in_buf, 1, MAX_READ_SIZE, fdin); + + if (ferror(fdin)) + { + return false; + } + } + + if (feof(fdin)) + { + action = LZMA_FINISH; + } + + ret = lzma_code(&strm, action); + + if ((strm.avail_out == 0) || (ret != LZMA_OK)) + { + const size_t write_size = MAX_READ_SIZE - strm.avail_out; + + if (fwrite(out_buf, 1, write_size, fdout) != write_size) + { + return false; + } + + strm.next_out = out_buf; + strm.avail_out = MAX_READ_SIZE; + } + + if (ret != LZMA_OK) + { + if (ret == LZMA_STREAM_END) + { + // lzma_stream_decoder() already guarantees that there's no trailing garbage. + assert(strm.avail_in == 0); + assert(action == LZMA_FINISH); + assert(feof(fdin)); + return false; + } + } + } + + return true; } diff --git lib/LibLzma2.hpp lib/LibLzma2.hpp index 440c182..b9efb42 100644 --- lib/LibLzma2.hpp +++ lib/LibLzma2.hpp @@ -1,6 +1,8 @@ -/** - * LibLzma.hpp - LibLzma.cpp header - **/ +/* + * + * LibLzma.hpp - LibLzma.cpp header + * +*/ #pragma once @@ -9,14 +11,15 @@ #include class NBXz { - public: - NBXz( QString, QString file = QString() ); - bool extract(); - static QString fileName; - static QString xzFileName; + public: + NBXz( QString, QString file = QString() ); + bool extract(); - private: - FILE *fdin, *fdout; - int NBXzError; + static QString fileName; + static QString xzFileName; + + private: + FILE *fdin, *fdout; + int NBXzError; }; diff --git lib/MimeHandler/NBMimeDatabase.cpp lib/MimeHandler/NBMimeDatabase.cpp new file mode 100644 index 0000000..926c047 --- /dev/null +++ lib/MimeHandler/NBMimeDatabase.cpp @@ -0,0 +1,704 @@ +/**************************************************************************** + ** + ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/ + ** + ** This file is part of the QtCore module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** GNU Lesser General Public License Usage + ** This file may be used under the terms of the GNU Lesser General Public + ** License version 2.1 as published by the Free Software Foundation and + ** appearing in the file LICENSE.LGPL included in the packaging of this + ** file. Please review the following information to ensure the GNU Lesser + ** General Public License version 2.1 requirements will be met: + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU General + ** Public License version 3.0 as published by the Free Software Foundation + ** and appearing in the file LICENSE.GPL included in the packaging of this + ** file. Please review the following information to ensure the GNU General + ** Public License version 3.0 requirements will be met: + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** Other Usage + ** Alternatively, this file may be used in accordance with the terms and + ** conditions contained in a signed written agreement between you and Nokia. + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include // always first + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +bool qt_isQMimeDatabaseDebuggingActivated(false); + +#ifndef QT_NO_DEBUG_OUTPUT +#define DBG() if (qt_isQMimeDatabaseDebuggingActivated) qDebug() << static_cast(this) << Q_FUNC_INFO +#else +#define DBG() if (0) qDebug() << static_cast(this) << Q_FUNC_INFO +#endif + +Q_GLOBAL_STATIC(QMimeDatabasePrivate, staticQMimeDatabase) + +QMimeDatabasePrivate *QMimeDatabasePrivate::instance() +{ + return staticQMimeDatabase(); +} + +QMimeDatabasePrivate::QMimeDatabasePrivate() + : m_provider(0), m_defaultMimeType(QLatin1String("application/octet-stream")) +{ +} + + +QMimeDatabasePrivate::~QMimeDatabasePrivate() +{ + delete m_provider; + m_provider = 0; +} + + +QMimeProviderBase *QMimeDatabasePrivate::provider() +{ + if (!m_provider) + { + QMimeProviderBase *binaryProvider = new QMimeBinaryProvider(this); + if (binaryProvider->isValid()) + { + m_provider = binaryProvider; + } + else + { + delete binaryProvider; + m_provider = new QMimeXMLProvider(this); + } + } + return m_provider; +} + + +void QMimeDatabasePrivate::setProvider(QMimeProviderBase *theProvider) +{ + delete m_provider; + m_provider = theProvider; +} + + +/*! + * \internal + * Returns a MIME type or an invalid one if none found + */ +QMimeType QMimeDatabasePrivate::mimeTypeForName(const QString& nameOrAlias) +{ + return provider()->mimeTypeForName(provider()->resolveAlias(nameOrAlias)); +} + + +QStringList QMimeDatabasePrivate::mimeTypeForFileName(const QString& fileName, QString *foundSuffix) +{ + if (fileName.endsWith(QLatin1Char('/'))) + { + return QStringList() << QLatin1String("inode/directory"); + } + + const QStringList matchingMimeTypes = provider()->findByFileName(QFileInfo(fileName).fileName(), foundSuffix); + + return matchingMimeTypes; +} + + +static inline bool isTextFile(const QByteArray& data) +{ + // UTF16 byte order marks + static const char bigEndianBOM[] = "\xFE\xFF"; + static const char littleEndianBOM[] = "\xFF\xFE"; + + if (data.startsWith(bigEndianBOM) || data.startsWith(littleEndianBOM)) + { + return true; + } + + // Check the first 32 bytes (see shared-mime spec) + const char *p = data.constData(); + const char *e = p + qMin(32, data.size()); + + for ( ; p < e; ++p) + { + if (((unsigned char)(*p) < 32) && (*p != 9) && (*p != 10) && (*p != 13)) + { + return false; + } + } + + return true; +} + + +QMimeType QMimeDatabasePrivate::findByData(const QByteArray& data, int *accuracyPtr) +{ + if (data.isEmpty()) + { + *accuracyPtr = 100; + return mimeTypeForName(QLatin1String("application/x-zerosize")); + } + + *accuracyPtr = 0; + QMimeType candidate = provider()->findByMagic(data, accuracyPtr); + + if (candidate.isValid()) + { + return candidate; + } + + if (isTextFile(data)) + { + *accuracyPtr = 5; + return mimeTypeForName(QLatin1String("text/plain")); + } + + return mimeTypeForName(defaultMimeType()); +} + + +QMimeType QMimeDatabasePrivate::mimeTypeForNameAndData(const QString& fileName, QIODevice *device, int *accuracyPtr) +{ + // First, glob patterns are evaluated. If there is a match with max weight, + // this one is selected and we are done. Otherwise, the file contents are + // evaluated and the match with the highest value (either a magic priority or + // a glob pattern weight) is selected. Matching starts from max level (most + // specific) in both cases, even when there is already a suffix matching candidate. + *accuracyPtr = 0; + + // Pass 1) Try to match on the file name + QStringList candidatesByName = mimeTypeForFileName(fileName); + + if (candidatesByName.count() == 1) + { + *accuracyPtr = 100; + const QMimeType mime = mimeTypeForName(candidatesByName.at(0)); + if (mime.isValid()) + { + return mime; + } + candidatesByName.clear(); + } + + // Extension is unknown, or matches multiple mimetypes. + // Pass 2) Match on content, if we can read the data + if (device->isOpen()) + { + // Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h). + // This is much faster than seeking back and forth into QIODevice. + const QByteArray data = device->peek(16384); + + int magicAccuracy = 0; + QMimeType candidateByData(findByData(data, &magicAccuracy)); + + // Disambiguate conflicting extensions (if magic matching found something) + if (candidateByData.isValid() && (magicAccuracy > 0)) + { + // "for glob_match in glob_matches:" + // "if glob_match is subclass or equal to sniffed_type, use glob_match" + const QString sniffedMime = candidateByData.name(); + foreach(const QString& m, candidatesByName) + { + if (inherits(m, sniffedMime)) + { + // We have magic + pattern pointing to this, so it's a pretty good match + *accuracyPtr = 100; + return mimeTypeForName(m); + } + } + *accuracyPtr = magicAccuracy; + return candidateByData; + } + } + + if (candidatesByName.count() > 1) + { + *accuracyPtr = 20; + candidatesByName.sort(); // to make it deterministic + const QMimeType mime = mimeTypeForName(candidatesByName.at(0)); + if (mime.isValid()) + { + return mime; + } + } + + return mimeTypeForName(defaultMimeType()); +} + + +QList QMimeDatabasePrivate::allMimeTypes() +{ + return provider()->allMimeTypes(); +} + + +bool QMimeDatabasePrivate::inherits(const QString& mime, const QString& parent) +{ + const QString resolvedParent = provider()->resolveAlias(parent); + //Q_ASSERT(provider()->resolveAlias(mime) == mime); + QStack toCheck; + + toCheck.push(mime); + while (!toCheck.isEmpty()) + { + const QString current = toCheck.pop(); + if (current == resolvedParent) + { + return true; + } + foreach(const QString& par, provider()->parents(current)) + toCheck.push(par); + } + return false; +} + + +/*! + * \class QMimeDatabase + * \brief The QMimeDatabase class maintains a database of MIME types. + * + * \since 5.0 + * + * The MIME type database is provided by the freedesktop.org shared-mime-info + * project. If the MIME type database cannot be found on the system, as is the case + * on most Windows and Mac OS X systems, Qt will use its own copy of it. + * + * Applications which want to define custom MIME types need to install an + * XML file into the locations searched for MIME definitions. + * These locations can be queried with + * \code + * QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/packages"), + * QStandardPaths::LocateDirectory); + * \endcode + * On a typical Unix system, this will be /usr/share/mime/packages/, but it is also + * possible to extend the list of directories by setting the environment variable + * XDG_DATA_DIRS. For instance adding /opt/myapp/share to XDG_DATA_DIRS will result + * in /opt/myapp/share/mime/packages/ being searched for MIME definitions. + * + * Here is an example of MIME XML: + * \code + * + * + * + * Qt qmake Profile + * + * + * + * \endcode + * + * For more details about the syntax of XML MIME definitions, including defining + * "magic" in order to detect MIME types based on data as well, read the + * Shared Mime Info specification at + * http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html + * + * On Unix systems, a binary cache is used for more performance. This cache is generated + * by the command "update-mime-database path", where path would be /opt/myapp/share/mime + * in the above example. Make sure to run this command when installing the MIME type + * definition file. + * + * \threadsafe + * + * \snippet doc/src/snippets/code/src_corelib_mimetype_qmimedatabase.cpp 0 + * + * \sa QMimeType + */ + +/*! + * \fn QMimeDatabase::QMimeDatabase(); + * Constructs this QMimeDatabase object. + */ +QMimeDatabase::QMimeDatabase() : + d(staticQMimeDatabase()) +{ + DBG(); +} + + +/*! + * \fn QMimeDatabase::~QMimeDatabase(); + * Destroys the QMimeDatabase object. + */ +QMimeDatabase::~QMimeDatabase() +{ + DBG(); + + d = 0; +} + + +/*! + * \fn QMimeType QMimeDatabase::mimeTypeForName(const QString &nameOrAlias) const; + * Returns a MIME type for \a nameOrAlias or an invalid one if none found. + */ +QMimeType QMimeDatabase::mimeTypeForName(const QString& nameOrAlias) const +{ + QMutexLocker locker(&d->mutex); + + return d->mimeTypeForName(nameOrAlias); +} + + +/*! + * Returns a MIME type for \a fileInfo. + * + * A valid MIME type is always returned. + * + * The default matching algorithm looks at both the file name and the file + * contents, if necessary. The file extension has priority over the contents, + * but the contents will be used if the file extension is unknown, or + * matches multiple MIME types. + * If \a fileInfo is a Unix symbolic link, the file that it refers to + * will be used instead. + * If the file doesn't match any known pattern or data, the default MIME type + * (application/octet-stream) is returned. + * + * When \a mode is set to MatchExtension, only the file name is used, not + * the file contents. The file doesn't even have to exist. If the file name + * doesn't match any known pattern, the default MIME type (application/octet-stream) + * is returned. + * If multiple MIME types match this file, the first one (alphabetically) is returned. + * + * When \a mode is set to MatchContent, and the file is readable, only the + * file contents are used to determine the MIME type. This is equivalent to + * calling mimeTypeForData with a QFile as input device. + * + * In all cases, the \a fileName can also include an absolute or relative path. + * + * \sa isDefault, mimeTypeForData + */ +QMimeType QMimeDatabase::mimeTypeForFile(const QFileInfo& fileInfo, MatchMode mode) const +{ + DBG() << "fileInfo" << fileInfo.absoluteFilePath(); + + QMutexLocker locker(&d->mutex); + + if (fileInfo.isDir()) + { + return d->mimeTypeForName(QLatin1String("inode/directory")); + } + + QFile file(fileInfo.absoluteFilePath()); + +#ifdef Q_OS_UNIX + // Cannot access statBuf.st_mode from the filesystem engine, so we have to stat again. + const QByteArray nativeFilePath = QFile::encodeName(file.fileName()); + QT_STATBUF statBuffer; + if (QT_LSTAT(nativeFilePath.constData(), &statBuffer) == 0) + { + if (S_ISCHR(statBuffer.st_mode)) + { + return d->mimeTypeForName(QLatin1String("inode/chardevice")); + } + if (S_ISBLK(statBuffer.st_mode)) + { + return d->mimeTypeForName(QLatin1String("inode/blockdevice")); + } + if (S_ISFIFO(statBuffer.st_mode)) + { + return d->mimeTypeForName(QLatin1String("inode/fifo")); + } + if (S_ISSOCK(statBuffer.st_mode)) + { + return d->mimeTypeForName(QLatin1String("inode/socket")); + } + } +#endif + + int priority = 0; + switch (mode) + { + case MatchDefault: + file.open(QIODevice::ReadOnly); // isOpen() will be tested by method below + return d->mimeTypeForNameAndData(fileInfo.absoluteFilePath(), &file, &priority); + + case MatchExtension: + locker.unlock(); + return mimeTypeForFile(fileInfo.absoluteFilePath(), mode); + + case MatchContent: + if (file.open(QIODevice::ReadOnly)) + { + locker.unlock(); + return mimeTypeForData(&file); + } + else + { + return d->mimeTypeForName(d->defaultMimeType()); + } + + default: + Q_ASSERT(false); + } + return d->mimeTypeForName(d->defaultMimeType()); +} + + +/*! + * Returns a MIME type for the file named \a fileName using \a mode. + * + * \overload + */ +QMimeType QMimeDatabase::mimeTypeForFile(const QString& fileName, MatchMode mode) const +{ + if (mode == MatchExtension) + { + QMutexLocker locker(&d->mutex); + QStringList matches = d->mimeTypeForFileName(fileName); + const int matchCount = matches.count(); + if (matchCount == 0) + { + return d->mimeTypeForName(d->defaultMimeType()); + } + else if (matchCount == 1) + { + return d->mimeTypeForName(matches.first()); + } + else + { + // We have to pick one. + matches.sort(); // Make it deterministic + return d->mimeTypeForName(matches.first()); + } + } + else + { + // Implemented as a wrapper around mimeTypeForFile(QFileInfo), so no mutex. + QFileInfo fileInfo(fileName); + return mimeTypeForFile(fileInfo); + } +} + + +/*! + * Returns the MIME types for the file name \a fileName. + * + * If the file name doesn't match any known pattern, an empty list is returned. + * If multiple MIME types match this file, they are all returned. + * + * This function does not try to open the file. To also use the content + * when determining the MIME type, use mimeTypeForFile() or + * mimeTypeForNameAndData() instead. + * + * \sa mimeTypeForFile + */ +QList QMimeDatabase::mimeTypesForFileName(const QString& fileName) const +{ + QMutexLocker locker(&d->mutex); + + QStringList matches = d->mimeTypeForFileName(fileName); + QList mimes; + + matches.sort(); // Make it deterministic + foreach(const QString& mime, matches) + mimes.append(d->mimeTypeForName(mime)); + return mimes; +} + + +/*! + * Returns the suffix for the file \a fileName, as known by the MIME database. + * + * This allows to pre-select "tar.bz2" for foo.tar.bz2, but still only + * "txt" for my.file.with.dots.txt. + */ +QString QMimeDatabase::suffixForFileName(const QString& fileName) const +{ + QMutexLocker locker(&d->mutex); + QString foundSuffix; + + d->mimeTypeForFileName(fileName, &foundSuffix); + return foundSuffix; +} + + +/*! + * Returns a MIME type for \a data. + * + * A valid MIME type is always returned. If \a data doesn't match any + * known MIME type data, the default MIME type (application/octet-stream) + * is returned. + */ +QMimeType QMimeDatabase::mimeTypeForData(const QByteArray& data) const +{ + QMutexLocker locker(&d->mutex); + + int accuracy = 0; + + return d->findByData(data, &accuracy); +} + + +/*! + * Returns a MIME type for the data in \a device. + * + * A valid MIME type is always returned. If the data in \a device doesn't match any + * known MIME type data, the default MIME type (application/octet-stream) + * is returned. + */ +QMimeType QMimeDatabase::mimeTypeForData(QIODevice *device) const +{ + QMutexLocker locker(&d->mutex); + + int accuracy = 0; + const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly); + + if (device->isOpen()) + { + // Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h). + // This is much faster than seeking back and forth into QIODevice. + const QByteArray data = device->peek(16384); + const QMimeType result = d->findByData(data, &accuracy); + if (openedByUs) + { + device->close(); + } + return result; + } + return d->mimeTypeForName(d->defaultMimeType()); +} + + +/*! + * Returns a MIME type for \a url. + * + * If the URL is a local file, this calls mimeTypeForFile. + * + * Otherwise the matching is done based on the file name only, + * except for schemes where file names don't mean much, like HTTP. + * This method always returns the default mimetype for HTTP URLs, + * use QNetworkAccessManager to handle HTTP URLs properly. + * + * A valid MIME type is always returned. If \a url doesn't match any + * known MIME type data, the default MIME type (application/octet-stream) + * is returned. + */ +QMimeType QMimeDatabase::mimeTypeForUrl(const QUrl& url) const +{ + if (url.scheme().compare("file", Qt::CaseInsensitive) == 0) + { + return mimeTypeForFile(url.toLocalFile()); + } + + const QString scheme = url.scheme(); + + if (scheme.startsWith(QLatin1String("http"))) + { + return mimeTypeForName(d->defaultMimeType()); + } + + return mimeTypeForFile(url.path()); +} + + +/*! + * Returns a MIME type for the given \a fileName and \a device data. + * + * This overload can be useful when the file is remote, and we started to + * download some of its data in a device. This allows to do full MIME type + * matching for remote files as well. + * + * If the device is not open, it will be opened by this function, and closed + * after the MIME type detection is completed. + * + * A valid MIME type is always returned. If \a device data doesn't match any + * known MIME type data, the default MIME type (application/octet-stream) + * is returned. + * + * This method looks at both the file name and the file contents, + * if necessary. The file extension has priority over the contents, + * but the contents will be used if the file extension is unknown, or + * matches multiple MIME types. + */ +QMimeType QMimeDatabase::mimeTypeForNameAndData(const QString& fileName, QIODevice *device) const +{ + DBG() << "fileName" << fileName; + + int accuracy = 0; + const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly); + const QMimeType result = d->mimeTypeForNameAndData(fileName, device, &accuracy); + + if (openedByUs) + { + device->close(); + } + return result; +} + + +/*! + * Returns a MIME type for the given \a fileName and device \a data. + * + * This overload can be useful when the file is remote, and we started to + * download some of its data. This allows to do full MIME type matching for + * remote files as well. + * + * A valid MIME type is always returned. If \a data doesn't match any + * known MIME type data, the default MIME type (application/octet-stream) + * is returned. + * + * This method looks at both the file name and the file contents, + * if necessary. The file extension has priority over the contents, + * but the contents will be used if the file extension is unknown, or + * matches multiple MIME types. + */ +QMimeType QMimeDatabase::mimeTypeForNameAndData(const QString& fileName, const QByteArray& data) const +{ + DBG() << "fileName" << fileName; + + QBuffer buffer(const_cast(&data)); + + buffer.open(QIODevice::ReadOnly); + int accuracy = 0; + + return d->mimeTypeForNameAndData(fileName, &buffer, &accuracy); +} + + +/*! + * Returns the list of all available MIME types. + * + * This can be useful for showing all MIME types to the user, for instance + * in a MIME type editor. Do not use unless really necessary in other cases + * though, prefer using the mimeTypeFor* methods for performance reasons. + */ +QList QMimeDatabase::allMimeTypes() const +{ + QMutexLocker locker(&d->mutex); + + return d->allMimeTypes(); +} + + +#undef DBG + +QT_END_NAMESPACE diff --git lib/MimeHandler/NBMimeDatabase.hpp lib/MimeHandler/NBMimeDatabase.hpp new file mode 100644 index 0000000..4eee5cd --- /dev/null +++ lib/MimeHandler/NBMimeDatabase.hpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMIMEDATABASE_H +#define QMIMEDATABASE_H + +#include +#include + +class QByteArray; +class QFileInfo; +class QIODevice; +class QUrl; + +class QMimeDatabasePrivate; +class QMimeDatabase +{ + Q_DISABLE_COPY(QMimeDatabase) + +public: + QMimeDatabase(); + ~QMimeDatabase(); + + QMimeType mimeTypeForName(const QString &nameOrAlias) const; + + enum MatchMode { + MatchDefault = 0x0, + MatchExtension = 0x1, + MatchContent = 0x2 + }; + + QMimeType mimeTypeForFile(const QString &fileName, MatchMode mode = MatchDefault) const; + QMimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode = MatchDefault) const; + QList mimeTypesForFileName(const QString &fileName) const; + + QMimeType mimeTypeForData(const QByteArray &data) const; + QMimeType mimeTypeForData(QIODevice *device) const; + + QMimeType mimeTypeForUrl(const QUrl &url) const; + QMimeType mimeTypeForNameAndData(const QString &fileName, QIODevice *device) const; + QMimeType mimeTypeForNameAndData(const QString &fileName, const QByteArray &data) const; + + QString suffixForFileName(const QString &fileName) const; + + QList allMimeTypes() const; + +private: + QMimeDatabasePrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QMIMEDATABASE_H diff --git lib/MimeHandler/NBMimeDatabase_p.hpp lib/MimeHandler/NBMimeDatabase_p.hpp new file mode 100644 index 0000000..88d57ff --- /dev/null +++ lib/MimeHandler/NBMimeDatabase_p.hpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMIMEDATABASE_P_H +#define QMIMEDATABASE_P_H + +#include +#include + +#include +#include +#include + +class QMimeDatabase; +class QMimeProviderBase; + +class QMimeDatabasePrivate +{ +public: + Q_DISABLE_COPY(QMimeDatabasePrivate) + + QMimeDatabasePrivate(); + ~QMimeDatabasePrivate(); + + static QMimeDatabasePrivate *instance(); + + QMimeProviderBase *provider(); + void setProvider(QMimeProviderBase *theProvider); + + inline QString defaultMimeType() const { return m_defaultMimeType; } + + bool inherits(const QString &mime, const QString &parent); + + QList allMimeTypes(); + + + QMimeType mimeTypeForName(const QString &nameOrAlias); + QMimeType mimeTypeForNameAndData(const QString &fileName, QIODevice *device, int *priorityPtr); + QMimeType findByData(const QByteArray &data, int *priorityPtr); + QStringList mimeTypeForFileName(const QString &fileName, QString *foundSuffix = 0); + + mutable QMimeProviderBase *m_provider; + const QString m_defaultMimeType; + QMutex mutex; +}; + +QT_END_NAMESPACE + +#endif // QMIMEDATABASE_P_H diff --git lib/MimeHandler/NBMimeGlobPattern.cpp lib/MimeHandler/NBMimeGlobPattern.cpp new file mode 100644 index 0000000..11f091c --- /dev/null +++ lib/MimeHandler/NBMimeGlobPattern.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** + ** + ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/ + ** + ** This file is part of the QtCore module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** GNU Lesser General Public License Usage + ** This file may be used under the terms of the GNU Lesser General Public + ** License version 2.1 as published by the Free Software Foundation and + ** appearing in the file LICENSE.LGPL included in the packaging of this + ** file. Please review the following information to ensure the GNU Lesser + ** General Public License version 2.1 requirements will be met: + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU General + ** Public License version 3.0 as published by the Free Software Foundation + ** and appearing in the file LICENSE.GPL included in the packaging of this + ** file. Please review the following information to ensure the GNU General + ** Public License version 3.0 requirements will be met: + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** Other Usage + ** Alternatively, this file may be used in accordance with the terms and + ** conditions contained in a signed written agreement between you and Nokia. + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include + +#include +#include +#include + +/*! + * \internal + * \class QMimeGlobMatchResult + * \brief The QMimeGlobMatchResult class accumulates results from glob matching. + * + * Handles glob weights, and preferring longer matches over shorter matches. + */ +void QMimeGlobMatchResult::addMatch(const QString& mimeType, int weight, const QString& pattern) +{ + // Is this a lower-weight pattern than the last match? Skip this match then. + if (weight < m_weight) + { + return; + } + bool replace = weight > m_weight; + + if (!replace) + { + // Compare the length of the match + if (pattern.length() < m_matchingPatternLength) + { + return; // too short, ignore + } + else if (pattern.length() > m_matchingPatternLength) + { + // longer: clear any previous match (like *.bz2, when pattern is *.tar.bz2) + replace = true; + } + } + if (replace) + { + m_matchingMimeTypes.clear(); + // remember the new "longer" length + m_matchingPatternLength = pattern.length(); + m_weight = weight; + } + m_matchingMimeTypes.append(mimeType); + if (pattern.startsWith(QLatin1String("*."))) + { + m_foundSuffix = pattern.mid(2); + } +} + + +/*! + * \internal + * \class QMimeGlobPattern + * \brief The QMimeGlobPattern class contains the glob pattern for file names for MIME type matching. + * + * \sa QMimeType, QMimeDatabase, QMimeMagicRuleMatcher, QMimeMagicRule + */ +bool QMimeGlobPattern::matchFileName(const QString& inputFilename) const +{ + // "Applications MUST match globs case-insensitively, except when the case-sensitive + // attribute is set to true." + // The constructor takes care of putting case-insensitive patterns in lowercase. + const QString filename = m_caseSensitivity == Qt::CaseInsensitive ? inputFilename.toLower() : inputFilename; + + const int pattern_len = m_pattern.length(); + + if (!pattern_len) + { + return false; + } + const int len = filename.length(); + + const int starCount = m_pattern.count(QLatin1Char('*')); + + // Patterns like "*~", "*.extension" + if ((m_pattern[0] == QLatin1Char('*')) && (m_pattern.indexOf(QLatin1Char('[')) == -1) && (starCount == 1)) + { + if (len + 1 < pattern_len) + { + return false; + } + + const QChar *c1 = m_pattern.unicode() + pattern_len - 1; + const QChar *c2 = filename.unicode() + len - 1; + int cnt = 1; + while (cnt < pattern_len && *c1-- == *c2--) + { + ++cnt; + } + return cnt == pattern_len; + } + + // Patterns like "README*" (well this is currently the only one like that...) + if ((starCount == 1) && (m_pattern.at(pattern_len - 1) == QLatin1Char('*'))) + { + if (len + 1 < pattern_len) + { + return false; + } + if (m_pattern.at(0) == QLatin1Char('*')) + { + return filename.indexOf(m_pattern.mid(1, pattern_len - 2)) != -1; + } + + const QChar *c1 = m_pattern.unicode(); + const QChar *c2 = filename.unicode(); + int cnt = 1; + while (cnt < pattern_len && *c1++ == *c2++) + { + ++cnt; + } + return cnt == pattern_len; + } + + // Names without any wildcards like "README" + if ((m_pattern.indexOf(QLatin1Char('[')) == -1) && (starCount == 0) && m_pattern.indexOf(QLatin1Char('?'))) + { + return(m_pattern == filename); + } + + // Other (quite rare) patterns, like "*.anim[1-9j]": use slow but correct method + const QRegExp rx(m_pattern, Qt::CaseSensitive, QRegExp::WildcardUnix); + + return rx.exactMatch(filename); +} + + +static bool isFastPattern(const QString& pattern) +{ + // starts with "*.", has no other '*' and no other '.' + return pattern.lastIndexOf(QLatin1Char('*')) == 0 && + pattern.lastIndexOf(QLatin1Char('.')) == 1 + // and contains no other special character + && !pattern.contains(QLatin1Char('?')) && + !pattern.contains(QLatin1Char('[')) + ; +} + + +void QMimeAllGlobPatterns::addGlob(const QMimeGlobPattern& glob) +{ + const QString& pattern = glob.pattern(); + + Q_ASSERT(!pattern.isEmpty()); + + // Store each patterns into either m_fastPatternDict (*.txt, *.html etc. with default weight 50) + // or for the rest, like core.*, *.tar.bz2, *~, into highWeightPatternOffset (>50) + // or lowWeightPatternOffset (<=50) + + if ((glob.weight() == 50) && isFastPattern(pattern) && !glob.isCaseSensitive()) + { + // The bulk of the patterns is *.foo with weight 50 --> those go into the fast patterns hash. + const QString extension = pattern.mid(2).toLower(); + QStringList& patterns = m_fastPatterns[extension]; // find or create + // This would just slow things down: if (!patterns.contains(glob.mimeType())) + patterns.append(glob.mimeType()); + } + else + { + if (glob.weight() > 50) + { + // This would just slow things down: if (!m_highWeightGlobs.hasPattern(glob.mimeType(), glob.pattern())) + m_highWeightGlobs.append(glob); + } + else + { + //This would just slow things down: if (!m_lowWeightGlobs.hasPattern(glob.mimeType(), glob.pattern())) + m_lowWeightGlobs.append(glob); + } + } +} + + +void QMimeAllGlobPatterns::removeMimeType(const QString& mimeType) +{ + QMutableHashIterator it(m_fastPatterns); + + while (it.hasNext()) + { + it.next().value().removeAll(mimeType); + } + m_highWeightGlobs.removeMimeType(mimeType); + m_lowWeightGlobs.removeMimeType(mimeType); +} + + +void QMimeGlobPatternList::match(QMimeGlobMatchResult& result, + const QString& fileName) const +{ + QMimeGlobPatternList::const_iterator it = this->constBegin(); + const QMimeGlobPatternList::const_iterator endIt = this->constEnd(); + + for ( ; it != endIt; ++it) + { + const QMimeGlobPattern& glob = *it; + if (glob.matchFileName(fileName)) + { + result.addMatch(glob.mimeType(), glob.weight(), glob.pattern()); + } + } +} + + +QStringList QMimeAllGlobPatterns::matchingGlobs(const QString& fileName, QString *foundSuffix) const +{ + // First try the high weight matches (>50), if any. + QMimeGlobMatchResult result; + + m_highWeightGlobs.match(result, fileName); + if (result.m_matchingMimeTypes.isEmpty()) + { + // Now use the "fast patterns" dict, for simple *.foo patterns with weight 50 + // (which is most of them, so this optimization is definitely worth it) + const int lastDot = fileName.lastIndexOf(QLatin1Char('.')); + if (lastDot != -1) // if no '.', skip the extension lookup + { + const int ext_len = fileName.length() - lastDot - 1; + const QString simpleExtension = fileName.right(ext_len).toLower(); + // (toLower because fast patterns are always case-insensitive and saved as lowercase) + + const QStringList matchingMimeTypes = m_fastPatterns.value(simpleExtension); + foreach(const QString& mime, matchingMimeTypes) + { + result.addMatch(mime, 50, QLatin1String("*.") + simpleExtension); + } + // Can't return yet; *.tar.bz2 has to win over *.bz2, so we need the low-weight mimetypes anyway, + // at least those with weight 50. + } + + // Finally, try the low weight matches (<=50) + m_lowWeightGlobs.match(result, fileName); + } + if (foundSuffix) + { + *foundSuffix = result.m_foundSuffix; + } + return result.m_matchingMimeTypes; +} + + +void QMimeAllGlobPatterns::clear() +{ + m_fastPatterns.clear(); + m_highWeightGlobs.clear(); + m_lowWeightGlobs.clear(); +} + + +QT_END_NAMESPACE diff --git lib/MimeHandler/NBMimeGlobPattern_p.hpp lib/MimeHandler/NBMimeGlobPattern_p.hpp new file mode 100644 index 0000000..c280b4f --- /dev/null +++ lib/MimeHandler/NBMimeGlobPattern_p.hpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMIMEGLOBPATTERN_P_H +#define QMIMEGLOBPATTERN_P_H + +#include +#include + +struct QMimeGlobMatchResult +{ + QMimeGlobMatchResult() + : m_weight(0), m_matchingPatternLength(0) + {} + + void addMatch(const QString &mimeType, int weight, const QString &pattern); + + QStringList m_matchingMimeTypes; + int m_weight; + int m_matchingPatternLength; + QString m_foundSuffix; +}; + +class QMimeGlobPattern +{ +public: + static const unsigned MaxWeight = 100; + static const unsigned DefaultWeight = 50; + static const unsigned MinWeight = 1; + + explicit QMimeGlobPattern(const QString &thePattern, const QString &theMimeType, unsigned theWeight = DefaultWeight, Qt::CaseSensitivity s = Qt::CaseInsensitive) : + m_pattern(thePattern), m_mimeType(theMimeType), m_weight(theWeight), m_caseSensitivity(s) + { + if (s == Qt::CaseInsensitive) { + m_pattern = m_pattern.toLower(); + } + } + ~QMimeGlobPattern() {} + + bool matchFileName(const QString &filename) const; + + inline const QString &pattern() const { return m_pattern; } + inline unsigned weight() const { return m_weight; } + inline const QString &mimeType() const { return m_mimeType; } + inline bool isCaseSensitive() const { return m_caseSensitivity == Qt::CaseSensitive; } + +private: + QString m_pattern; + QString m_mimeType; + int m_weight; + Qt::CaseSensitivity m_caseSensitivity; +}; + +class QMimeGlobPatternList : public QList +{ +public: + bool hasPattern(const QString &mimeType, const QString &pattern) const + { + const_iterator it = begin(); + const const_iterator myend = end(); + for (; it != myend; ++it) + if ((*it).pattern() == pattern && (*it).mimeType() == mimeType) + return true; + return false; + } + + /*! + "noglobs" is very rare occurrence, so it's ok if it's slow + */ + void removeMimeType(const QString &mimeType) + { + QMutableListIterator it(*this); + while (it.hasNext()) { + if (it.next().mimeType() == mimeType) + it.remove(); + } + } + + void match(QMimeGlobMatchResult &result, const QString &fileName) const; +}; + +/*! + \internal + Result of the globs parsing, as data structures ready for efficient MIME type matching. + This contains: + 1) a map of fast regular patterns (e.g. *.txt is stored as "txt" in a qhash's key) + 2) a linear list of high-weight globs + 3) a linear list of low-weight globs + */ +class QMimeAllGlobPatterns +{ +public: + typedef QHash PatternsMap; // MIME type -> patterns + + void addGlob(const QMimeGlobPattern &glob); + void removeMimeType(const QString &mimeType); + QStringList matchingGlobs(const QString &fileName, QString *foundSuffix) const; + void clear(); + + PatternsMap m_fastPatterns; // example: "doc" -> "application/msword", "text/plain" + QMimeGlobPatternList m_highWeightGlobs; + QMimeGlobPatternList m_lowWeightGlobs; // <= 50, including the non-fast 50 patterns +}; + +QT_END_NAMESPACE + +#endif // QMIMEGLOBPATTERN_P_H diff --git lib/MimeHandler/NBMimeMagicRule.cpp lib/MimeHandler/NBMimeMagicRule.cpp new file mode 100644 index 0000000..0132b96 --- /dev/null +++ lib/MimeHandler/NBMimeMagicRule.cpp @@ -0,0 +1,479 @@ +/**************************************************************************** + ** + ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/ + ** + ** This file is part of the QtCore module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** GNU Lesser General Public License Usage + ** This file may be used under the terms of the GNU Lesser General Public + ** License version 2.1 as published by the Free Software Foundation and + ** appearing in the file LICENSE.LGPL included in the packaging of this + ** file. Please review the following information to ensure the GNU Lesser + ** General Public License version 2.1 requirements will be met: + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU General + ** Public License version 3.0 as published by the Free Software Foundation + ** and appearing in the file LICENSE.GPL included in the packaging of this + ** file. Please review the following information to ensure the GNU General + ** Public License version 3.0 requirements will be met: + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** Other Usage + ** Alternatively, this file may be used in accordance with the terms and + ** conditions contained in a signed written agreement between you and Nokia. + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + + +#define QT_NO_CAST_FROM_ASCII + +#include + +#include +#include +#include + +// in the same order as Type! +static const char magicRuleTypes_string[] = + "invalid\0" + "string\0" + "host16\0" + "host32\0" + "big16\0" + "big32\0" + "little16\0" + "little32\0" + "byte\0" + "\0"; + +static const int magicRuleTypes_indices[] = { + 0, 8, 15, 22, 29, 35, 41, 50, 59, 65, 0 +}; + +QMimeMagicRule::Type QMimeMagicRule::type(const QByteArray& theTypeName) +{ + for (int i = String; i <= Byte; ++i) + { + if (theTypeName == magicRuleTypes_string + magicRuleTypes_indices[i]) + { + return Type(i); + } + } + return Invalid; +} + + +QByteArray QMimeMagicRule::typeName(QMimeMagicRule::Type theType) +{ + return magicRuleTypes_string + magicRuleTypes_indices[theType]; +} + + +class QMimeMagicRulePrivate +{ +public: + bool operator==(const QMimeMagicRulePrivate& other) const; + + QMimeMagicRule::Type type; + QByteArray value; + int startPos; + int endPos; + QByteArray mask; + + QByteArray pattern; + quint32 number; + quint32 numberMask; + + typedef bool (*MatchFunction)(const QMimeMagicRulePrivate *d, const QByteArray& data); + MatchFunction matchFunction; +}; + +bool QMimeMagicRulePrivate::operator==(const QMimeMagicRulePrivate& other) const +{ + return type == other.type && + value == other.value && + startPos == other.startPos && + endPos == other.endPos && + mask == other.mask && + pattern == other.pattern && + number == other.number && + numberMask == other.numberMask && + matchFunction == other.matchFunction; +} + + +// Used by both providers +bool QMimeMagicRule::matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength, + int valueLength, const char *valueData, const char *mask) +{ + // Size of searched data. + // Example: value="ABC", rangeLength=3 -> we need 3+3-1=5 bytes (ABCxx,xABCx,xxABC would match) + const int dataNeeded = qMin(rangeLength + valueLength - 1, dataSize - rangeStart); + + if (!mask) + { + // callgrind says QByteArray::indexOf is much slower, since our strings are typically too + // short for be worth Boyer-Moore matching (1 to 71 bytes, 11 bytes on average). + bool found = false; + for (int i = rangeStart; i < rangeStart + rangeLength; ++i) + { + if (i + valueLength > dataSize) + { + break; + } + + if (memcmp(valueData, dataPtr + i, valueLength) == 0) + { + found = true; + break; + } + } + if (!found) + { + return false; + } + } + else + { + bool found = false; + const char *readDataBase = dataPtr + rangeStart; + // Example (continued from above): + // deviceSize is 4, so dataNeeded was max'ed to 4. + // maxStartPos = 4 - 3 + 1 = 2, and indeed + // we need to check for a match a positions 0 and 1 (ABCx and xABC). + const int maxStartPos = dataNeeded - valueLength + 1; + for (int i = 0; i < maxStartPos; ++i) + { + const char *d = readDataBase + i; + bool valid = true; + for (int idx = 0; idx < valueLength; ++idx) + { + if (((*d++) & mask[idx]) != (valueData[idx] & mask[idx])) + { + valid = false; + break; + } + } + if (valid) + { + found = true; + } + } + if (!found) + { + return false; + } + } + //qDebug() << "Found" << value << "in" << searchedData; + return true; +} + + +static bool matchString(const QMimeMagicRulePrivate *d, const QByteArray& data) +{ + const int rangeLength = d->endPos - d->startPos + 1; + + return QMimeMagicRule::matchSubstring(data.constData(), data.size(), d->startPos, rangeLength, d->pattern.size(), d->pattern.constData(), d->mask.constData()); +} + + +template +static bool matchNumber(const QMimeMagicRulePrivate *d, const QByteArray& data) +{ + const T value(d->number); + const T mask(d->numberMask); + + //qDebug() << "matchNumber" << "0x" << QString::number(d->number, 16) << "size" << sizeof(T); + //qDebug() << "mask" << QString::number(d->numberMask, 16); + + const char *p = data.constData() + d->startPos; + const char *e = data.constData() + qMin(data.size() - int(sizeof(T)), d->endPos + 1); + + for ( ; p <= e; ++p) + { + if ((*reinterpret_cast(p) & mask) == (value & mask)) + { + return true; + } + } + + return false; +} + + +static inline QByteArray makePattern(const QByteArray& value) +{ + QByteArray pattern(value.size(), Qt::Uninitialized); + char *data = pattern.data(); + + const char *p = value.constData(); + const char *e = p + value.size(); + + for ( ; p < e; ++p) + { + if ((*p == '\\') && (++p < e)) + { + if (*p == 'x') // hex (\\xff) + { + char c = 0; + for (int i = 0; i < 2 && p + 1 < e; ++i) + { + ++p; + if ((*p >= '0') && (*p <= '9')) + { + c = (c << 4) + *p - '0'; + } + else if ((*p >= 'a') && (*p <= 'f')) + { + c = (c << 4) + *p - 'a' + 10; + } + else if ((*p >= 'A') && (*p <= 'F')) + { + c = (c << 4) + *p - 'A' + 10; + } + else + { + continue; + } + } + *data++ = c; + } + else if ((*p >= '0') && (*p <= '7')) // oct (\\7, or \\77, or \\377) + { + char c = *p - '0'; + if ((p + 1 < e) && (p[1] >= '0') && (p[1] <= '7')) + { + c = (c << 3) + *(++p) - '0'; + if ((p + 1 < e) && (p[1] >= '0') && (p[1] <= '7') && (p[-1] <= '3')) + { + c = (c << 3) + *(++p) - '0'; + } + } + *data++ = c; + } + else if (*p == 'n') + { + *data++ = '\n'; + } + else if (*p == 'r') + { + *data++ = '\r'; + } + else // escaped + { + *data++ = *p; + } + } + else + { + *data++ = *p; + } + } + pattern.truncate(data - pattern.data()); + + return pattern; +} + + +QMimeMagicRule::QMimeMagicRule(QMimeMagicRule::Type theType, + const QByteArray& theValue, + int theStartPos, + int theEndPos, + const QByteArray& theMask) : + d(new QMimeMagicRulePrivate) +{ + Q_ASSERT(!theValue.isEmpty()); + + d->type = theType; + d->value = theValue; + d->startPos = theStartPos; + d->endPos = theEndPos; + d->mask = theMask; + d->matchFunction = 0; + + if ((d->type >= Host16) && (d->type <= Byte)) + { + bool ok; + d->number = d->value.toUInt(&ok, 0); // autodetect + Q_ASSERT(ok); + d->numberMask = !d->mask.isEmpty() ? d->mask.toUInt(&ok, 0) : 0; // autodetect + } + + switch (d->type) + { + case String: + d->pattern = makePattern(d->value); + d->pattern.squeeze(); + if (!d->mask.isEmpty()) + { + Q_ASSERT(d->mask.size() >= 4 && d->mask.startsWith("0x")); + d->mask = QByteArray::fromHex(QByteArray::fromRawData(d->mask.constData() + 2, d->mask.size() - 2)); + Q_ASSERT(d->mask.size() == d->pattern.size()); + } + else + { + d->mask.fill(char(-1), d->pattern.size()); + } + d->mask.squeeze(); + d->matchFunction = matchString; + break; + + case Byte: + if (d->number <= quint8(-1)) + { + if (d->numberMask == 0) + { + d->numberMask = quint8(-1); + } + d->matchFunction = matchNumber; + } + break; + + case Big16: + case Host16: + case Little16: + if (d->number <= quint16(-1)) + { + d->number = d->type == Little16 ? qFromLittleEndian(d->number) : qFromBigEndian(d->number); + if (d->numberMask == 0) + { + d->numberMask = quint16(-1); + } + d->matchFunction = matchNumber; + } + break; + + case Big32: + case Host32: + case Little32: + if (d->number <= quint32(-1)) + { + d->number = d->type == Little32 ? qFromLittleEndian(d->number) : qFromBigEndian(d->number); + if (d->numberMask == 0) + { + d->numberMask = quint32(-1); + } + d->matchFunction = matchNumber; + } + break; + + default: + break; + } +} + + +QMimeMagicRule::QMimeMagicRule(const QMimeMagicRule& other) : + d(new QMimeMagicRulePrivate(*other.d)) +{ +} + + +QMimeMagicRule::~QMimeMagicRule() +{ +} + + +QMimeMagicRule& QMimeMagicRule::operator=(const QMimeMagicRule& other) +{ + *d = *other.d; + return *this; +} + + +bool QMimeMagicRule::operator==(const QMimeMagicRule& other) const +{ + return d == other.d || + *d == *other.d; +} + + +QMimeMagicRule::Type QMimeMagicRule::type() const +{ + return d->type; +} + + +QByteArray QMimeMagicRule::value() const +{ + return d->value; +} + + +int QMimeMagicRule::startPos() const +{ + return d->startPos; +} + + +int QMimeMagicRule::endPos() const +{ + return d->endPos; +} + + +QByteArray QMimeMagicRule::mask() const +{ + QByteArray result = d->mask; + + if (d->type == String) + { + // restore '0x' + result = "0x" + result.toHex(); + } + return result; +} + + +bool QMimeMagicRule::isValid() const +{ + return d->matchFunction; +} + + +bool QMimeMagicRule::matches(const QByteArray& data) const +{ + const bool ok = d->matchFunction && d->matchFunction(d.data(), data); + + if (!ok) + { + return false; + } + + // No submatch? Then we are done. + if (m_subMatches.isEmpty()) + { + return true; + } + + //qDebug() << "Checking" << m_subMatches.count() << "sub-rules"; + // Check that one of the submatches matches too + for (QList::const_iterator it = m_subMatches.begin(), end = m_subMatches.end(); + it != end; ++it) + { + if ((*it).matches(data)) + { + // One of the hierarchies matched -> mimetype recognized. + return true; + } + } + return false; +} + + +QT_END_NAMESPACE diff --git lib/MimeHandler/NBMimeMagicRuleMatcher.cpp lib/MimeHandler/NBMimeMagicRuleMatcher.cpp new file mode 100644 index 0000000..431cda8 --- /dev/null +++ lib/MimeHandler/NBMimeMagicRuleMatcher.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** + ** + ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/ + ** + ** This file is part of the QtCore module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** GNU Lesser General Public License Usage + ** This file may be used under the terms of the GNU Lesser General Public + ** License version 2.1 as published by the Free Software Foundation and + ** appearing in the file LICENSE.LGPL included in the packaging of this + ** file. Please review the following information to ensure the GNU Lesser + ** General Public License version 2.1 requirements will be met: + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU General + ** Public License version 3.0 as published by the Free Software Foundation + ** and appearing in the file LICENSE.GPL included in the packaging of this + ** file. Please review the following information to ensure the GNU General + ** Public License version 3.0 requirements will be met: + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** Other Usage + ** Alternatively, this file may be used in accordance with the terms and + ** conditions contained in a signed written agreement between you and Nokia. + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#define QT_NO_CAST_FROM_ASCII + +#include +#include + +/*! + * \internal + * \class QMimeMagicRuleMatcher + * + * \brief The QMimeMagicRuleMatcher class checks a number of rules based on operator "or". + * + * It is used for rules parsed from XML files. + * + * \sa QMimeType, QMimeDatabase, MagicRule, MagicStringRule, MagicByteRule, GlobPattern + * \sa QMimeTypeParserBase, MimeTypeParser + */ + +QMimeMagicRuleMatcher::QMimeMagicRuleMatcher(const QString& mime, unsigned thePriority) : + m_list(), + m_priority(thePriority), + m_mimetype(mime) +{ +} + + +bool QMimeMagicRuleMatcher::operator==(const QMimeMagicRuleMatcher& other) +{ + return m_list == other.m_list && + m_priority == other.m_priority; +} + + +void QMimeMagicRuleMatcher::addRule(const QMimeMagicRule& rule) +{ + m_list.append(rule); +} + + +void QMimeMagicRuleMatcher::addRules(const QList& rules) +{ + m_list.append(rules); +} + + +QList QMimeMagicRuleMatcher::magicRules() const +{ + return m_list; +} + + +// Check for a match on contents of a file +bool QMimeMagicRuleMatcher::matches(const QByteArray& data) const +{ + foreach(const QMimeMagicRule& magicRule, m_list) + { + if (magicRule.matches(data)) + { + return true; + } + } + + return false; +} + + +// Return a priority value from 1..100 +unsigned QMimeMagicRuleMatcher::priority() const +{ + return m_priority; +} + + +QT_END_NAMESPACE diff --git lib/MimeHandler/NBMimeMagicRuleMatcher_p.hpp lib/MimeHandler/NBMimeMagicRuleMatcher_p.hpp new file mode 100644 index 0000000..6ba1834 --- /dev/null +++ lib/MimeHandler/NBMimeMagicRuleMatcher_p.hpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMIMEMAGICRULEMATCHER_P_H +#define QMIMEMAGICRULEMATCHER_P_H + +#include +#include +#include + +#include + +class QMimeMagicRuleMatcher +{ +public: + explicit QMimeMagicRuleMatcher(const QString &mime, unsigned priority = 65535); + + bool operator==(const QMimeMagicRuleMatcher &other); + + void addRule(const QMimeMagicRule &rule); + void addRules(const QList &rules); + QList magicRules() const; + + bool matches(const QByteArray &data) const; + + unsigned priority() const; + + QString mimetype() const { return m_mimetype; } + +private: + QList m_list; + unsigned m_priority; + QString m_mimetype; +}; + +QT_END_NAMESPACE + +#endif // QMIMEMAGICRULEMATCHER_P_H diff --git lib/MimeHandler/NBMimeMagicRule_p.hpp lib/MimeHandler/NBMimeMagicRule_p.hpp new file mode 100644 index 0000000..e074943 --- /dev/null +++ lib/MimeHandler/NBMimeMagicRule_p.hpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMIMEMAGICRULE_P_H +#define QMIMEMAGICRULE_P_H + +#include +#include +#include + +class QMimeMagicRulePrivate; +class QMimeMagicRule +{ +public: + enum Type { Invalid = 0, String, Host16, Host32, Big16, Big32, Little16, Little32, Byte }; + + QMimeMagicRule(Type type, const QByteArray &value, int startPos, int endPos, const QByteArray &mask = QByteArray()); + QMimeMagicRule(const QMimeMagicRule &other); + ~QMimeMagicRule(); + + QMimeMagicRule &operator=(const QMimeMagicRule &other); + + bool operator==(const QMimeMagicRule &other) const; + + Type type() const; + QByteArray value() const; + int startPos() const; + int endPos() const; + QByteArray mask() const; + + bool isValid() const; + + bool matches(const QByteArray &data) const; + + QList m_subMatches; + + static Type type(const QByteArray &type); + static QByteArray typeName(Type type); + + static bool matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength, int valueLength, const char *valueData, const char *mask); + +private: + const QScopedPointer d; +}; +Q_DECLARE_TYPEINFO(QMimeMagicRule, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif // QMIMEMAGICRULE_H diff --git lib/MimeHandler/NBMimeProvider.cpp lib/MimeHandler/NBMimeProvider.cpp new file mode 100644 index 0000000..6a99853 --- /dev/null +++ lib/MimeHandler/NBMimeProvider.cpp @@ -0,0 +1,1059 @@ +/**************************************************************************** + ** + ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/ + ** + ** This file is part of the QtCore module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** GNU Lesser General Public License Usage + ** This file may be used under the terms of the GNU Lesser General Public + ** License version 2.1 as published by the Free Software Foundation and + ** appearing in the file LICENSE.LGPL included in the packaging of this + ** file. Please review the following information to ensure the GNU Lesser + ** General Public License version 2.1 requirements will be met: + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU General + ** Public License version 3.0 as published by the Free Software Foundation + ** and appearing in the file LICENSE.GPL included in the packaging of this + ** file. Please review the following information to ensure the GNU General + ** Public License version 3.0 requirements will be met: + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** Other Usage + ** Alternatively, this file may be used in accordance with the terms and + ** conditions contained in a signed written agreement between you and Nokia. + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static QString fallbackParent(const QString& mimeTypeName) +{ + const QString myGroup = mimeTypeName.left(mimeTypeName.indexOf(QLatin1Char('/'))); + + // All text/* types are subclasses of text/plain. + if ((myGroup == QLatin1String("text")) && (mimeTypeName != QLatin1String("text/plain"))) + { + return QLatin1String("text/plain"); + } + // All real-file mimetypes implicitly derive from application/octet-stream + if ((myGroup != QLatin1String("inode")) && + // ignore non-file extensions + (myGroup != QLatin1String("all")) && (myGroup != QLatin1String("fonts")) && (myGroup != QLatin1String("print")) && (myGroup != QLatin1String("uri")) && + (mimeTypeName != QLatin1String("application/octet-stream"))) + { + return QLatin1String("application/octet-stream"); + } + return QString(); +} + + +QMimeProviderBase::QMimeProviderBase(QMimeDatabasePrivate *db) + : m_db(db) +{ +} + + +int qmime_secondsBetweenChecks = 5; + +bool QMimeProviderBase::shouldCheck() +{ + const QDateTime now = QDateTime::currentDateTime(); + + if (m_lastCheck.isValid() && (m_lastCheck.secsTo(now) < qmime_secondsBetweenChecks)) + { + return false; + } + m_lastCheck = now; + return true; +} + + +QMimeBinaryProvider::QMimeBinaryProvider(QMimeDatabasePrivate *db) + : QMimeProviderBase(db), m_mimetypeListLoaded(false) +{ +} + + +#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY) +#define QT_USE_MMAP +#endif + +struct QMimeBinaryProvider::CacheFile +{ + CacheFile(const QString& fileName); + ~CacheFile(); + + bool isValid() const { return m_valid; } + inline quint16 getUint16(int offset) const + { + return qFromBigEndian(*reinterpret_cast(data + offset)); + } + + inline quint32 getUint32(int offset) const + { + return qFromBigEndian(*reinterpret_cast(data + offset)); + } + + inline const char *getCharStar(int offset) const + { + return reinterpret_cast(data + offset); + } + + bool load(); + bool reload(); + + QFile file; + uchar *data; + QDateTime m_mtime; + bool m_valid; +}; + +QMimeBinaryProvider::CacheFile::CacheFile(const QString& fileName) + : file(fileName), m_valid(false) +{ + load(); +} + + +QMimeBinaryProvider::CacheFile::~CacheFile() +{ +} + + +bool QMimeBinaryProvider::CacheFile::load() +{ + if (!file.open(QIODevice::ReadOnly)) + { + return false; + } + data = file.map(0, file.size()); + if (data) + { + const int major = getUint16(0); + const int minor = getUint16(2); + m_valid = (major == 1 && minor >= 1 && minor <= 2); + } + m_mtime = QFileInfo(file).lastModified(); + return m_valid; +} + + +bool QMimeBinaryProvider::CacheFile::reload() +{ + //qDebug() << "reload!" << file->fileName(); + m_valid = false; + if (file.isOpen()) + { + file.close(); + } + data = 0; + return load(); +} + + +QMimeBinaryProvider::CacheFile *QMimeBinaryProvider::CacheFileList::findCacheFile(const QString& fileName) const +{ + for (const_iterator it = begin(); it != end(); ++it) + { + if ((*it)->file.fileName() == fileName) + { + return *it; + } + } + return 0; +} + + +QMimeBinaryProvider::~QMimeBinaryProvider() +{ + qDeleteAll(m_cacheFiles); +} + + +// Position of the "list offsets" values, at the beginning of the mime.cache file +enum +{ + PosAliasListOffset = 4, + PosParentListOffset = 8, + PosLiteralListOffset = 12, + PosReverseSuffixTreeOffset = 16, + PosGlobListOffset = 20, + PosMagicListOffset = 24, + // PosNamespaceListOffset = 28, + PosIconsListOffset = 32, + PosGenericIconsListOffset = 36 +}; + +bool QMimeBinaryProvider::isValid() +{ +#if defined(QT_USE_MMAP) + if (!qgetenv("QT_NO_MIME_CACHE").isEmpty()) + { + return false; + } + + Q_ASSERT(m_cacheFiles.isEmpty()); // this method is only ever called once + checkCache(); + + if (m_cacheFiles.count() > 1) + { + return true; + } + if (m_cacheFiles.isEmpty()) + { + return false; + } + + // We found exactly one file; is it the user-modified mimes, or a system file? + const QString foundFile = m_cacheFiles.first()->file.fileName(); + const QString localCacheFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/mime/mime.cache"); + + return foundFile != localCacheFile; +#else + return false; +#endif +} + + +bool QMimeBinaryProvider::CacheFileList::checkCacheChanged() +{ + bool somethingChanged = false; + QMutableListIterator it(*this); + + while (it.hasNext()) + { + CacheFile *cacheFile = it.next(); + QFileInfo fileInfo(cacheFile->file); + if (!fileInfo.exists()) // This can't happen by just running update-mime-database. But the user could use rm -rf :-) + { + delete cacheFile; + it.remove(); + somethingChanged = true; + } + else if (fileInfo.lastModified() > cacheFile->m_mtime) + { + if (!cacheFile->reload()) + { + delete cacheFile; + it.remove(); + } + somethingChanged = true; + } + } + return somethingChanged; +} + + +void QMimeBinaryProvider::checkCache() +{ + if (!shouldCheck()) + { + return; + } + + // First iterate over existing known cache files and check for uptodate + if (m_cacheFiles.checkCacheChanged()) + { + m_mimetypeListLoaded = false; + } + + // Then check if new cache files appeared + const QStringList cacheFileNames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/mime.cache")); + + if (cacheFileNames != m_cacheFileNames) + { + foreach(const QString& cacheFileName, cacheFileNames) + { + CacheFile *cacheFile = m_cacheFiles.findCacheFile(cacheFileName); + + if (!cacheFile) + { + //qDebug() << "new file:" << cacheFileName; + cacheFile = new CacheFile(cacheFileName); + if (cacheFile->isValid()) // verify version + { + m_cacheFiles.append(cacheFile); + } + else + { + delete cacheFile; + } + } + } + m_cacheFileNames = cacheFileNames; + m_mimetypeListLoaded = false; + } +} + + +static QMimeType mimeTypeForNameUnchecked(const QString& name) +{ + QMimeTypePrivate data; + + data.name = name; + // The rest is retrieved on demand. + // comment and globPatterns: in loadMimeTypePrivate + // iconName: in loadIcon + // genericIconName: in loadGenericIcon + return QMimeType(data); +} + + +QMimeType QMimeBinaryProvider::mimeTypeForName(const QString& name) +{ + checkCache(); + if (!m_mimetypeListLoaded) + { + loadMimeTypeList(); + } + if (!m_mimetypeNames.contains(name)) + { + return QMimeType(); // unknown mimetype + } + return mimeTypeForNameUnchecked(name); +} + + +QStringList QMimeBinaryProvider::findByFileName(const QString& fileName, QString *foundSuffix) +{ + checkCache(); + if (fileName.isEmpty()) + { + return QStringList(); + } + const QString lowerFileName = fileName.toLower(); + QMimeGlobMatchResult result; + + // TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly. + foreach(CacheFile * cacheFile, m_cacheFiles) + { + matchGlobList(result, cacheFile, cacheFile->getUint32(PosLiteralListOffset), fileName); + matchGlobList(result, cacheFile, cacheFile->getUint32(PosGlobListOffset), fileName); + const int reverseSuffixTreeOffset = cacheFile->getUint32(PosReverseSuffixTreeOffset); + const int numRoots = cacheFile->getUint32(reverseSuffixTreeOffset); + const int firstRootOffset = cacheFile->getUint32(reverseSuffixTreeOffset + 4); + + matchSuffixTree(result, cacheFile, numRoots, firstRootOffset, lowerFileName, fileName.length() - 1, false); + if (result.m_matchingMimeTypes.isEmpty()) + { + matchSuffixTree(result, cacheFile, numRoots, firstRootOffset, fileName, fileName.length() - 1, true); + } + } + if (foundSuffix) + { + *foundSuffix = result.m_foundSuffix; + } + return result.m_matchingMimeTypes; +} + + +void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult& result, CacheFile *cacheFile, int off, const QString& fileName) +{ + const int numGlobs = cacheFile->getUint32(off); + + //qDebug() << "Loading" << numGlobs << "globs from" << cacheFile->file.fileName() << "at offset" << cacheFile->globListOffset; + for (int i = 0; i < numGlobs; ++i) + { + const int globOffset = cacheFile->getUint32(off + 4 + 12 * i); + const int mimeTypeOffset = cacheFile->getUint32(off + 4 + 12 * i + 4); + const int flagsAndWeight = cacheFile->getUint32(off + 4 + 12 * i + 8); + const int weight = flagsAndWeight & 0xff; + const bool caseSensitive = flagsAndWeight & 0x100; + const Qt::CaseSensitivity qtCaseSensitive = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + const QString pattern = QLatin1String(cacheFile->getCharStar(globOffset)); + + const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); + //qDebug() << pattern << mimeType << weight << caseSensitive; + QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive); + + // TODO: this could be done faster for literals where a simple == would do. + if (glob.matchFileName(fileName)) + { + result.addMatch(QLatin1String(mimeType), weight, pattern); + } + } +} + + +bool QMimeBinaryProvider::matchSuffixTree(QMimeGlobMatchResult& result, QMimeBinaryProvider::CacheFile *cacheFile, int numEntries, int firstOffset, const QString& fileName, int charPos, bool caseSensitiveCheck) +{ + QChar fileChar = fileName[charPos]; + int min = 0; + int max = numEntries - 1; + + while (min <= max) + { + const int mid = (min + max) / 2; + const int off = firstOffset + 12 * mid; + const QChar ch = cacheFile->getUint32(off); + if (ch < fileChar) + { + min = mid + 1; + } + else if (ch > fileChar) + { + max = mid - 1; + } + else + { + --charPos; + int numChildren = cacheFile->getUint32(off + 4); + int childrenOffset = cacheFile->getUint32(off + 8); + bool success = false; + if (charPos > 0) + { + success = matchSuffixTree(result, cacheFile, numChildren, childrenOffset, fileName, charPos, caseSensitiveCheck); + } + if (!success) + { + for (int i = 0; i < numChildren; ++i) + { + const int childOff = childrenOffset + 12 * i; + const int mch = cacheFile->getUint32(childOff); + if (mch != 0) + { + break; + } + const int mimeTypeOffset = cacheFile->getUint32(childOff + 4); + const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); + const int flagsAndWeight = cacheFile->getUint32(childOff + 8); + const int weight = flagsAndWeight & 0xff; + const bool caseSensitive = flagsAndWeight & 0x100; + if (caseSensitiveCheck || !caseSensitive) + { + result.addMatch(QLatin1String(mimeType), weight, QLatin1Char('*') + fileName.mid(charPos + 1)); + success = true; + } + } + } + return success; + } + } + return false; +} + + +bool QMimeBinaryProvider::matchMagicRule(QMimeBinaryProvider::CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray& data) +{ + const char *dataPtr = data.constData(); + const int dataSize = data.size(); + + for (int matchlet = 0; matchlet < numMatchlets; ++matchlet) + { + const int off = firstOffset + matchlet * 32; + const int rangeStart = cacheFile->getUint32(off); + const int rangeLength = cacheFile->getUint32(off + 4); + //const int wordSize = cacheFile->getUint32(off + 8); + const int valueLength = cacheFile->getUint32(off + 12); + const int valueOffset = cacheFile->getUint32(off + 16); + const int maskOffset = cacheFile->getUint32(off + 20); + const char *mask = maskOffset ? cacheFile->getCharStar(maskOffset) : NULL; + + if (!QMimeMagicRule::matchSubstring(dataPtr, dataSize, rangeStart, rangeLength, valueLength, cacheFile->getCharStar(valueOffset), mask)) + { + continue; + } + + const int numChildren = cacheFile->getUint32(off + 24); + const int firstChildOffset = cacheFile->getUint32(off + 28); + if (numChildren == 0) // No submatch? Then we are done. + { + return true; + } + // Check that one of the submatches matches too + if (matchMagicRule(cacheFile, numChildren, firstChildOffset, data)) + { + return true; + } + } + return false; +} + + +QMimeType QMimeBinaryProvider::findByMagic(const QByteArray& data, int *accuracyPtr) +{ + checkCache(); + foreach(CacheFile * cacheFile, m_cacheFiles) + { + const int magicListOffset = cacheFile->getUint32(PosMagicListOffset); + const int numMatches = cacheFile->getUint32(magicListOffset); + //const int maxExtent = cacheFile->getUint32(magicListOffset + 4); + const int firstMatchOffset = cacheFile->getUint32(magicListOffset + 8); + + for (int i = 0; i < numMatches; ++i) + { + const int off = firstMatchOffset + i * 16; + const int numMatchlets = cacheFile->getUint32(off + 8); + const int firstMatchletOffset = cacheFile->getUint32(off + 12); + if (matchMagicRule(cacheFile, numMatchlets, firstMatchletOffset, data)) + { + const int mimeTypeOffset = cacheFile->getUint32(off + 4); + const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); + *accuracyPtr = cacheFile->getUint32(off); + // Return the first match. We have no rules for conflicting magic data... + // (mime.cache itself is sorted, but what about local overrides with a lower prio?) + return mimeTypeForNameUnchecked(QLatin1String(mimeType)); + } + } + } + return QMimeType(); +} + + +QStringList QMimeBinaryProvider::parents(const QString& mime) +{ + checkCache(); + const QByteArray mimeStr = mime.toLatin1(); + QStringList result; + + foreach(CacheFile * cacheFile, m_cacheFiles) + { + const int parentListOffset = cacheFile->getUint32(PosParentListOffset); + const int numEntries = cacheFile->getUint32(parentListOffset); + + int begin = 0; + int end = numEntries - 1; + + while (begin <= end) + { + const int medium = (begin + end) / 2; + const int off = parentListOffset + 4 + 8 * medium; + const int mimeOffset = cacheFile->getUint32(off); + const char *aMime = cacheFile->getCharStar(mimeOffset); + const int cmp = qstrcmp(aMime, mimeStr); + if (cmp < 0) + { + begin = medium + 1; + } + else if (cmp > 0) + { + end = medium - 1; + } + else + { + const int parentsOffset = cacheFile->getUint32(off + 4); + const int numParents = cacheFile->getUint32(parentsOffset); + for (int i = 0; i < numParents; ++i) + { + const int parentOffset = cacheFile->getUint32(parentsOffset + 4 + 4 * i); + const char *aParent = cacheFile->getCharStar(parentOffset); + result.append(QString::fromLatin1(aParent)); + } + break; + } + } + } + if (result.isEmpty()) + { + const QString parent = fallbackParent(mime); + if (!parent.isEmpty()) + { + result.append(parent); + } + } + return result; +} + + +QString QMimeBinaryProvider::resolveAlias(const QString& name) +{ + checkCache(); + const QByteArray input = name.toLatin1(); + + foreach(CacheFile * cacheFile, m_cacheFiles) + { + const int aliasListOffset = cacheFile->getUint32(PosAliasListOffset); + const int numEntries = cacheFile->getUint32(aliasListOffset); + int begin = 0; + int end = numEntries - 1; + + while (begin <= end) + { + const int medium = (begin + end) / 2; + const int off = aliasListOffset + 4 + 8 * medium; + const int aliasOffset = cacheFile->getUint32(off); + const char *alias = cacheFile->getCharStar(aliasOffset); + const int cmp = qstrcmp(alias, input); + if (cmp < 0) + { + begin = medium + 1; + } + else if (cmp > 0) + { + end = medium - 1; + } + else + { + const int mimeOffset = cacheFile->getUint32(off + 4); + const char *mimeType = cacheFile->getCharStar(mimeOffset); + return QLatin1String(mimeType); + } + } + } + + return name; +} + + +void QMimeBinaryProvider::loadMimeTypeList() +{ + if (!m_mimetypeListLoaded) + { + m_mimetypeListLoaded = true; + m_mimetypeNames.clear(); + // Unfortunately mime.cache doesn't have a full list of all mimetypes. + // So we have to parse the plain-text files called "types". + const QStringList typesFilenames = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/types")); + foreach(const QString& typeFilename, typesFilenames) + { + QFile file(typeFilename); + + if (file.open(QIODevice::ReadOnly)) + { + while (!file.atEnd()) + { + QByteArray line = file.readLine(); + line.chop(1); + m_mimetypeNames.insert(QString::fromLatin1(line.constData(), line.size())); + } + } + } + } +} + + +QList QMimeBinaryProvider::allMimeTypes() +{ + QList result; + + loadMimeTypeList(); + + for (QSet::const_iterator it = m_mimetypeNames.constBegin(); + it != m_mimetypeNames.constEnd(); ++it) + { + result.append(mimeTypeForNameUnchecked(*it)); + } + + return result; +} + + +void QMimeBinaryProvider::loadMimeTypePrivate(QMimeTypePrivate& data) +{ + // load comment and globPatterns + + const QString file = data.name + QLatin1String(".xml"); + const QStringList mimeFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QString::fromLatin1("mime/") + file); + + if (mimeFiles.isEmpty()) + { + // TODO: ask Thiago about this + qWarning() << "No file found for" << file << ", even though the file appeared in a directory listing."; + qWarning() << "Either it was just removed, or the directory doesn't have executable permission..."; + qWarning() << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory); + return; + } + + QString comment; + QString mainPattern; + const QString preferredLanguage = QLocale::system().name(); + + QListIterator mimeFilesIter(mimeFiles); + + mimeFilesIter.toBack(); + while (mimeFilesIter.hasPrevious()) // global first, then local. + { + const QString fullPath = mimeFilesIter.previous(); + QFile qfile(fullPath); + if (!qfile.open(QFile::ReadOnly)) + { + continue; + } + + QXmlStreamReader xml(&qfile); + if (xml.readNextStartElement()) + { + if (xml.name() != QLatin1String("mime-type")) + { + continue; + } + const QString name = xml.attributes().value(QLatin1String("type")).toString(); + if (name.isEmpty()) + { + continue; + } + if (name != data.name) + { + qWarning() << "Got name" << name << "in file" << file << "expected" << data.name; + } + + while (xml.readNextStartElement()) + { + const QStringRef tag = xml.name(); + if (tag == QLatin1String("comment")) + { + QString lang = xml.attributes().value(QLatin1String("xml:lang")).toString(); + const QString text = xml.readElementText(); + if (lang.isEmpty()) + { + lang = QLatin1String("en_US"); + } + data.localeComments.insert(lang, text); + continue; // we called readElementText, so we're at the EndElement already. + } + else if (tag == QLatin1String("icon")) // as written out by shared-mime-info >= 0.40 + { + data.iconName = xml.attributes().value(QLatin1String("name")).toString(); + } + else if (tag == QLatin1String("glob-deleteall")) // as written out by shared-mime-info >= 0.70 + { + data.globPatterns.clear(); + } + else if (tag == QLatin1String("glob")) // as written out by shared-mime-info >= 0.70 + { + const QString pattern = xml.attributes().value(QLatin1String("pattern")).toString(); + if (mainPattern.isEmpty() && pattern.startsWith(QLatin1Char('*'))) + { + mainPattern = pattern; + } + if (!data.globPatterns.contains(pattern)) + { + data.globPatterns.append(pattern); + } + } + xml.skipCurrentElement(); + } + Q_ASSERT(xml.name() == QLatin1String("mime-type")); + } + } + + // Let's assume that shared-mime-info is at least version 0.70 + // Otherwise we would need 1) a version check, and 2) code for parsing patterns from the globs file. +#if 1 + if (!mainPattern.isEmpty() && (data.globPatterns.first() != mainPattern)) + { + // ensure it's first in the list of patterns + data.globPatterns.removeAll(mainPattern); + data.globPatterns.prepend(mainPattern); + } +#else + const bool globsInXml = sharedMimeInfoVersion() >= QT_VERSION_CHECK(0, 70, 0); + if (globsInXml) + { + if (!mainPattern.isEmpty() && (data.globPatterns.first() != mainPattern)) + { + // ensure it's first in the list of patterns + data.globPatterns.removeAll(mainPattern); + data.globPatterns.prepend(mainPattern); + } + } + else + { + // Fallback: get the patterns from the globs file + // TODO: This would be the only way to support shared-mime-info < 0.70 + // But is this really worth the effort? + } +#endif +} + + +// Binary search in the icons or generic-icons list +QString QMimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray& inputMime) +{ + const int iconsListOffset = cacheFile->getUint32(posListOffset); + const int numIcons = cacheFile->getUint32(iconsListOffset); + int begin = 0; + int end = numIcons - 1; + + while (begin <= end) + { + const int medium = (begin + end) / 2; + const int off = iconsListOffset + 4 + 8 * medium; + const int mimeOffset = cacheFile->getUint32(off); + const char *mime = cacheFile->getCharStar(mimeOffset); + const int cmp = qstrcmp(mime, inputMime); + if (cmp < 0) + { + begin = medium + 1; + } + else if (cmp > 0) + { + end = medium - 1; + } + else + { + const int iconOffset = cacheFile->getUint32(off + 4); + return QLatin1String(cacheFile->getCharStar(iconOffset)); + } + } + return QString(); +} + + +void QMimeBinaryProvider::loadIcon(QMimeTypePrivate& data) +{ + checkCache(); + const QByteArray inputMime = data.name.toLatin1(); + + foreach(CacheFile * cacheFile, m_cacheFiles) + { + const QString icon = iconForMime(cacheFile, PosIconsListOffset, inputMime); + + if (!icon.isEmpty()) + { + data.iconName = icon; + return; + } + } +} + + +void QMimeBinaryProvider::loadGenericIcon(QMimeTypePrivate& data) +{ + checkCache(); + const QByteArray inputMime = data.name.toLatin1(); + + foreach(CacheFile * cacheFile, m_cacheFiles) + { + const QString icon = iconForMime(cacheFile, PosGenericIconsListOffset, inputMime); + + if (!icon.isEmpty()) + { + data.genericIconName = icon; + return; + } + } +} + + +//// + +QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db) + : QMimeProviderBase(db), m_loaded(false) +{ +} + + +bool QMimeXMLProvider::isValid() +{ + return true; +} + + +QMimeType QMimeXMLProvider::mimeTypeForName(const QString& name) +{ + ensureLoaded(); + + return m_nameMimeTypeMap.value(name); +} + + +QStringList QMimeXMLProvider::findByFileName(const QString& fileName, QString *foundSuffix) +{ + ensureLoaded(); + + const QStringList matchingMimeTypes = m_mimeTypeGlobs.matchingGlobs(fileName, foundSuffix); + + return matchingMimeTypes; +} + + +QMimeType QMimeXMLProvider::findByMagic(const QByteArray& data, int *accuracyPtr) +{ + ensureLoaded(); + + QString candidate; + + foreach(const QMimeMagicRuleMatcher& matcher, m_magicMatchers) + { + if (matcher.matches(data)) + { + const int priority = matcher.priority(); + if (priority > *accuracyPtr) + { + *accuracyPtr = priority; + candidate = matcher.mimetype(); + } + } + } + return mimeTypeForName(candidate); +} + + +void QMimeXMLProvider::ensureLoaded() +{ + if (!m_loaded || shouldCheck()) + { + bool fdoXmlFound = false; + QStringList allFiles; + + const QStringList packageDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime/packages"), QStandardPaths::LocateDirectory); + //qDebug() << "packageDirs=" << packageDirs; + foreach(const QString& packageDir, packageDirs) + { + QDir dir(packageDir); + const QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); + + //qDebug() << static_cast(this) << Q_FUNC_INFO << packageDir << files; + if (!fdoXmlFound) + { + fdoXmlFound = files.contains(QLatin1String("freedesktop.org.xml")); + } + QStringList::const_iterator endIt(files.constEnd()); + + for (QStringList::const_iterator it(files.constBegin()); it != endIt; ++it) + { + allFiles.append(packageDir + QLatin1Char('/') + *it); + } + } + + // if (!fdoXmlFound) { + // We could instead install the file as part of installing Qt? + // allFiles.prepend( QLatin1String( DATA_DIR ) + QLatin1String( "/freedesktop.org.xml" ) ); + // } + + if (m_allFiles == allFiles) + { + return; + } + m_allFiles = allFiles; + + m_nameMimeTypeMap.clear(); + m_aliases.clear(); + m_parents.clear(); + m_mimeTypeGlobs.clear(); + m_magicMatchers.clear(); + + //qDebug() << "Loading" << m_allFiles; + + foreach(const QString& file, allFiles) + load(file); + } +} + + +void QMimeXMLProvider::load(const QString& fileName) +{ + QString errorMessage; + + if (!load(fileName, &errorMessage)) + { + qWarning("QMimeDatabase: Error loading %s\n%s", qPrintable(fileName), qPrintable(errorMessage)); + } +} + + +bool QMimeXMLProvider::load(const QString& fileName, QString *errorMessage) +{ + m_loaded = true; + + QFile file(fileName); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + if (errorMessage) + { + *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(fileName, file.errorString()); + } + return false; + } + + if (errorMessage) + { + errorMessage->clear(); + } + + QMimeTypeParser parser(*this); + + return parser.parse(&file, fileName, errorMessage); +} + + +void QMimeXMLProvider::addGlobPattern(const QMimeGlobPattern& glob) +{ + m_mimeTypeGlobs.addGlob(glob); +} + + +void QMimeXMLProvider::addMimeType(const QMimeType& mt) +{ + m_nameMimeTypeMap.insert(mt.name(), mt); +} + + +QStringList QMimeXMLProvider::parents(const QString& mime) +{ + ensureLoaded(); + QStringList result = m_parents.value(mime); + + if (result.isEmpty()) + { + const QString parent = fallbackParent(mime); + if (!parent.isEmpty()) + { + result.append(parent); + } + } + return result; +} + + +void QMimeXMLProvider::addParent(const QString& child, const QString& parent) +{ + m_parents[child].append(parent); +} + + +QString QMimeXMLProvider::resolveAlias(const QString& name) +{ + ensureLoaded(); + return m_aliases.value(name, name); +} + + +void QMimeXMLProvider::addAlias(const QString& alias, const QString& name) +{ + m_aliases.insert(alias, name); +} + + +QList QMimeXMLProvider::allMimeTypes() +{ + ensureLoaded(); + return m_nameMimeTypeMap.values(); +} + + +void QMimeXMLProvider::addMagicMatcher(const QMimeMagicRuleMatcher& matcher) +{ + m_magicMatchers.append(matcher); +} + + +QT_END_NAMESPACE diff --git lib/MimeHandler/NBMimeProvider_p.hpp lib/MimeHandler/NBMimeProvider_p.hpp new file mode 100644 index 0000000..23bfe13 --- /dev/null +++ lib/MimeHandler/NBMimeProvider_p.hpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMIMEPROVIDER_P_H +#define QMIMEPROVIDER_P_H + +#include +#include +#include + +class QMimeMagicRuleMatcher; + +class QMimeProviderBase +{ +public: + QMimeProviderBase(QMimeDatabasePrivate *db); + virtual ~QMimeProviderBase() {} + + virtual bool isValid() = 0; + virtual QMimeType mimeTypeForName(const QString &name) = 0; + virtual QStringList findByFileName(const QString &fileName, QString *foundSuffix) = 0; + virtual QStringList parents(const QString &mime) = 0; + virtual QString resolveAlias(const QString &name) = 0; + virtual QMimeType findByMagic(const QByteArray &data, int *accuracyPtr) = 0; + virtual QList allMimeTypes() = 0; + virtual void loadMimeTypePrivate(QMimeTypePrivate &) {} + virtual void loadIcon(QMimeTypePrivate &) {} + virtual void loadGenericIcon(QMimeTypePrivate &) {} + + QMimeDatabasePrivate *m_db; +protected: + bool shouldCheck(); + QDateTime m_lastCheck; +}; + +/* + Parses the files 'mime.cache' and 'types' on demand + */ +class QMimeBinaryProvider : public QMimeProviderBase +{ +public: + QMimeBinaryProvider(QMimeDatabasePrivate *db); + virtual ~QMimeBinaryProvider(); + + virtual bool isValid(); + virtual QMimeType mimeTypeForName(const QString &name); + virtual QStringList findByFileName(const QString &fileName, QString *foundSuffix); + virtual QStringList parents(const QString &mime); + virtual QString resolveAlias(const QString &name); + virtual QMimeType findByMagic(const QByteArray &data, int *accuracyPtr); + virtual QList allMimeTypes(); + virtual void loadMimeTypePrivate(QMimeTypePrivate &); + virtual void loadIcon(QMimeTypePrivate &); + virtual void loadGenericIcon(QMimeTypePrivate &); + +private: + struct CacheFile; + + void matchGlobList(QMimeGlobMatchResult &result, CacheFile *cacheFile, int offset, const QString &fileName); + bool matchSuffixTree(QMimeGlobMatchResult &result, CacheFile *cacheFile, int numEntries, int firstOffset, const QString &fileName, int charPos, bool caseSensitiveCheck); + bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data); + QString iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime); + void loadMimeTypeList(); + void checkCache(); + + class CacheFileList : public QList + { + public: + CacheFile *findCacheFile(const QString &fileName) const; + bool checkCacheChanged(); + }; + CacheFileList m_cacheFiles; + QStringList m_cacheFileNames; + QSet m_mimetypeNames; + bool m_mimetypeListLoaded; +}; + +/* + Parses the raw XML files (slower) + */ +class QMimeXMLProvider : public QMimeProviderBase +{ +public: + QMimeXMLProvider(QMimeDatabasePrivate *db); + + virtual bool isValid(); + virtual QMimeType mimeTypeForName(const QString &name); + virtual QStringList findByFileName(const QString &fileName, QString *foundSuffix); + virtual QStringList parents(const QString &mime); + virtual QString resolveAlias(const QString &name); + virtual QMimeType findByMagic(const QByteArray &data, int *accuracyPtr); + virtual QList allMimeTypes(); + + bool load(const QString &fileName, QString *errorMessage); + + // Called by the mimetype xml parser + void addMimeType(const QMimeType &mt); + void addGlobPattern(const QMimeGlobPattern &glob); + void addParent(const QString &child, const QString &parent); + void addAlias(const QString &alias, const QString &name); + void addMagicMatcher(const QMimeMagicRuleMatcher &matcher); + +private: + void ensureLoaded(); + void load(const QString &fileName); + + bool m_loaded; + + typedef QHash NameMimeTypeMap; + NameMimeTypeMap m_nameMimeTypeMap; + + typedef QHash AliasHash; + AliasHash m_aliases; + + typedef QHash ParentsHash; + ParentsHash m_parents; + QMimeAllGlobPatterns m_mimeTypeGlobs; + + QList m_magicMatchers; + QStringList m_allFiles; +}; + +QT_END_NAMESPACE + +#endif // QMIMEPROVIDER_P_H diff --git lib/MimeHandler/NBMimeType.cpp lib/MimeHandler/NBMimeType.cpp new file mode 100644 index 0000000..0b83d17 --- /dev/null +++ lib/MimeHandler/NBMimeType.cpp @@ -0,0 +1,537 @@ +/**************************************************************************** + ** + ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/ + ** + ** This file is part of the QtCore module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** GNU Lesser General Public License Usage + ** This file may be used under the terms of the GNU Lesser General Public + ** License version 2.1 as published by the Free Software Foundation and + ** appearing in the file LICENSE.LGPL included in the packaging of this + ** file. Please review the following information to ensure the GNU Lesser + ** General Public License version 2.1 requirements will be met: + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU General + ** Public License version 3.0 as published by the Free Software Foundation + ** and appearing in the file LICENSE.GPL included in the packaging of this + ** file. Please review the following information to ensure the GNU General + ** Public License version 3.0 requirements will be met: + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** Other Usage + ** Alternatively, this file may be used in accordance with the terms and + ** conditions contained in a signed written agreement between you and Nokia. + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +bool qt_isQMimeTypeDebuggingActivated(false); + +#ifndef QT_NO_DEBUG_OUTPUT +#define DBG() if (qt_isQMimeTypeDebuggingActivated) qDebug() << static_cast(this) << Q_FUNC_INFO +#else +#define DBG() if (0) qDebug() << static_cast(this) << Q_FUNC_INFO +#endif + +QMimeTypePrivate::QMimeTypePrivate() +{ +} + + +QMimeTypePrivate::QMimeTypePrivate(const QMimeType& other) + : name(other.d->name), + localeComments(other.d->localeComments), + genericIconName(other.d->genericIconName), + iconName(other.d->iconName), + globPatterns(other.d->globPatterns) +{ +} + + +void QMimeTypePrivate::clear() +{ + name.clear(); + localeComments.clear(); + genericIconName.clear(); + iconName.clear(); + globPatterns.clear(); +} + + +/*! + * \fn bool QMimeTypePrivate::operator==(const QMimeTypePrivate &other) const; + * Returns true if \a other equals this QMimeTypePrivate object, otherwise returns false. + */ +bool QMimeTypePrivate::operator==(const QMimeTypePrivate& other) const +{ + DBG(); + if ((name == other.name) && + (localeComments == other.localeComments) && + (genericIconName == other.genericIconName) && + (iconName == other.iconName) && + (globPatterns == other.globPatterns)) + { + return true; + } + + DBG() << name << other.name << (name == other.name); + //DBG() << comment << other.comment << (comment == other.comment); + DBG() << localeComments << other.localeComments << (localeComments == other.localeComments); + DBG() << genericIconName << other.genericIconName << (genericIconName == other.genericIconName); + DBG() << iconName << other.iconName << (iconName == other.iconName); + DBG() << globPatterns << other.globPatterns << (globPatterns == other.globPatterns); + return false; +} + + +void QMimeTypePrivate::addGlobPattern(const QString& pattern) +{ + globPatterns.append(pattern); +} + + +/*! + * \class QMimeType + * \brief The QMimeType class describes types of file or data, represented by a MIME type string. + * + * \since 5.0 + * + * For instance a file named "readme.txt" has the MIME type "text/plain". + * The MIME type can be determined from the file name, or from the file + * contents, or from both. MIME type determination can also be done on + * buffers of data not coming from files. + * + * Determining the MIME type of a file can be useful to make sure your + * application supports it. It is also useful in file-manager-like applications + * or widgets, in order to display an appropriate icon() for the file, or even + * the descriptive comment() in detailed views. + * + * To check if a file has the expected MIME type, you should use inherits() + * rather than a simple string comparison based on the name(). This is because + * MIME types can inherit from each other: for instance a C source file is + * a specific type of plain text file, so text/x-csrc inherits text/plain. + * + * \sa QMimeDatabase + */ + +/*! + * \fn QMimeType::QMimeType(); + * Constructs this QMimeType object initialized with default property values that indicate an invalid MIME type. + */ +QMimeType::QMimeType() : + d(new QMimeTypePrivate()) +{ + DBG() << "name():" << name(); + //DBG() << "aliases():" << aliases(); + //DBG() << "comment():" << comment(); + DBG() << "genericIconName():" << genericIconName(); + DBG() << "iconName():" << iconName(); + DBG() << "globPatterns():" << globPatterns(); + DBG() << "suffixes():" << suffixes(); + DBG() << "preferredSuffix():" << preferredSuffix(); +} + + +/*! + * \fn QMimeType::QMimeType(const QMimeType &other); + * Constructs this QMimeType object as a copy of \a other. + */ +QMimeType::QMimeType(const QMimeType& other) : + d(other.d) +{ + DBG() << "name():" << name(); + //DBG() << "aliases():" << aliases(); + //DBG() << "comment():" << comment(); + DBG() << "genericIconName():" << genericIconName(); + DBG() << "iconName():" << iconName(); + DBG() << "globPatterns():" << globPatterns(); + DBG() << "suffixes():" << suffixes(); + DBG() << "preferredSuffix():" << preferredSuffix(); +} + + +/*! + * \fn QMimeType &QMimeType::operator=(const QMimeType &other); + * Assigns the data of \a other to this QMimeType object, and returns a reference to this object. + */ +QMimeType& QMimeType::operator=(const QMimeType& other) +{ + if (d != other.d) + { + d = other.d; + } + return *this; +} + + +/*! + * \fn QMimeType::QMimeType(const QMimeTypePrivate &dd); + * Assigns the data of the QMimeTypePrivate \a dd to this QMimeType object, and returns a reference to this object. + */ +QMimeType::QMimeType(const QMimeTypePrivate& dd) : + d(new QMimeTypePrivate(dd)) +{ + DBG() << "name():" << name(); + //DBG() << "aliases():" << aliases(); + //DBG() << "comment():" << comment(); + DBG() << "genericIconName():" << genericIconName(); + DBG() << "iconName():" << iconName(); + DBG() << "globPatterns():" << globPatterns(); + DBG() << "suffixes():" << suffixes(); + DBG() << "preferredSuffix():" << preferredSuffix(); +} + + +/*! + * \fn void QMimeType::swap(QMimeType &other); + * Swaps QMimeType \a other with this QMimeType object. + * + * This operation is very fast and never fails. + * + * The swap() method helps with the implementation of assignment + * operators in an exception-safe way. For more information consult + * \l {http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap} + * {More C++ Idioms - Copy-and-swap}. + */ + +/*! + * \fn QMimeType::~QMimeType(); + * Destroys the QMimeType object, and releases the d pointer. + */ +QMimeType::~QMimeType() +{ + DBG() << "name():" << name(); + //DBG() << "aliases():" << aliases(); + //DBG() << "comment():" << comment(); + DBG() << "genericIconName():" << genericIconName(); + DBG() << "iconName():" << iconName(); + DBG() << "globPatterns():" << globPatterns(); + DBG() << "suffixes():" << suffixes(); + DBG() << "preferredSuffix():" << preferredSuffix(); +} + + +/*! + * \fn bool QMimeType::operator==(const QMimeType &other) const; + * Returns true if \a other equals this QMimeType object, otherwise returns false. + */ +bool QMimeType::operator==(const QMimeType& other) const +{ + return d == other.d || *d == *other.d; +} + + +/*! + * \fn bool QMimeType::operator!=(const QMimeType &other) const; + * Returns true if \a other does not equal this QMimeType object, otherwise returns false. + */ + +/*! + * \fn bool QMimeType::isValid() const; + * Returns true if the QMimeType object contains valid data, otherwise returns false. + * A valid MIME type has a non-empty name(). + * The invalid MIME type is the default-constructed QMimeType. + */ +bool QMimeType::isValid() const +{ + return !d->name.isEmpty(); +} + + +/*! + * \fn bool QMimeType::isDefault() const; + * Returns true if this MIME type is the default MIME type which + * applies to all files: application/octet-stream. + */ +bool QMimeType::isDefault() const +{ + return d->name == QMimeDatabasePrivate::instance()->defaultMimeType(); +} + + +/*! + * \fn QString QMimeType::name() const; + * Returns the name of the MIME type. + */ +QString QMimeType::name() const +{ + return d->name; +} + + +/*! + * Returns the description of the MIME type to be displayed on user interfaces. + * + * The system language (QLocale::system().name()) is used to select the appropriate translation. + */ +QString QMimeType::comment() const +{ + QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d); + + QStringList languageList; + + languageList << QLocale::system().name(); + /*temporally workaround until i learn the code behind the qt 4.8 api for locales */ +#if QT_VERSION >= 0x040800 + languageList << QLocale::system().uiLanguages(); +#endif + Q_FOREACH (const QString& language, languageList) + { + const QString lang = language == QLatin1String("C") ? QLatin1String("en_US") : language; + const QString comm = d->localeComments.value(lang); + if (!comm.isEmpty()) + { + return comm; + } + const int pos = lang.indexOf(QLatin1Char('_')); + if (pos != -1) + { + // "pt_BR" not found? try just "pt" + const QString shortLang = lang.left(pos); + const QString commShort = d->localeComments.value(shortLang); + if (!commShort.isEmpty()) + { + return commShort; + } + } + } + + // Use the mimetype name as fallback + return d->name; +} + + +/*! + * \fn QString QMimeType::genericIconName() const; + * Returns the file name of a generic icon that represents the MIME type. + * + * This should be used if the icon returned by iconName() cannot be found on + * the system. It is used for categories of similar types (like spreadsheets + * or archives) that can use a common icon. + * The freedesktop.org Icon Naming Specification lists a set of such icon names. + * + * The icon name can be given to QIcon::fromTheme() in order to load the icon. + */ +QString QMimeType::genericIconName() const +{ + QMimeDatabasePrivate::instance()->provider()->loadGenericIcon(*d); + if (d->genericIconName.isEmpty()) + { + // From the spec: + // If the generic icon name is empty (not specified by the mimetype definition) + // then the mimetype is used to generate the generic icon by using the top-level + // media type (e.g. "video" in "video/ogg") and appending "-x-generic" + // (i.e. "video-x-generic" in the previous example). + QString group = name(); + const int slashindex = group.indexOf(QLatin1Char('/')); + if (slashindex != -1) + { + group = group.left(slashindex); + } + return group + QLatin1String("-x-generic"); + } + return d->genericIconName; +} + + +/*! + * \fn QString QMimeType::iconName() const; + * Returns the file name of an icon image that represents the MIME type. + * + * The icon name can be given to QIcon::fromTheme() in order to load the icon. + */ +QString QMimeType::iconName() const +{ + QMimeDatabasePrivate::instance()->provider()->loadIcon(*d); + if (d->iconName.isEmpty()) + { + // Make default icon name from the mimetype name + d->iconName = name(); + const int slashindex = d->iconName.indexOf(QLatin1Char('/')); + if (slashindex != -1) + { + d->iconName[slashindex] = QLatin1Char('-'); + } + } + return d->iconName; +} + + +/*! + * \fn QStringList QMimeType::globPatterns() const; + * Returns the list of glob matching patterns. + */ +QStringList QMimeType::globPatterns() const +{ + QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d); + return d->globPatterns; +} + + +/*! + * A type is a subclass of another type if any instance of the first type is + * also an instance of the second. For example, all image/svg+xml files are also + * text/xml, text/plain and application/octet-stream files. Subclassing is about + * the format, rather than the category of the data (for example, there is no + * 'generic spreadsheet' class that all spreadsheets inherit from). + * Conversely, the parent mimetype of image/svg+xml is text/xml. + * + * A mimetype can have multiple parents. For instance application/x-perl + * has two parents: application/x-executable and text/plain. This makes + * it possible to both execute perl scripts, and to open them in text editors. + */ +QStringList QMimeType::parentMimeTypes() const +{ + return QMimeDatabasePrivate::instance()->provider()->parents(d->name); +} + + +static void collectParentMimeTypes(const QString& mime, QStringList& allParents) +{ + QStringList parents = QMimeDatabasePrivate::instance()->provider()->parents(mime); + + foreach(const QString& parent, parents) + { + // I would use QSet, but since order matters I better not + if (!allParents.contains(parent)) + { + allParents.append(parent); + } + } + // We want a breadth-first search, so that the least-specific parent (octet-stream) is last + // This means iterating twice, unfortunately. + foreach(const QString& parent, parents) + { + collectParentMimeTypes(parent, allParents); + } +} + + +/*! + * Return all the parent mimetypes of this mimetype, direct and indirect. + * This includes the parent(s) of its parent(s), etc. + * + * For instance, for image/svg+xml the list would be: + * application/xml, text/plain, application/octet-stream. + * + * Note that application/octet-stream is the ultimate parent for all types + * of files (but not directories). + */ +QStringList QMimeType::allAncestors() const +{ + QStringList allParents; + + collectParentMimeTypes(d->name, allParents); + return allParents; +} + + +/*! + * \fn QStringList QMimeType::suffixes() const; + * Returns the known suffixes for the MIME type. + */ +QStringList QMimeType::suffixes() const +{ + QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d); + + QStringList result; + + foreach(const QString& pattern, d->globPatterns) + { + // Not a simple suffix if if looks like: README or *. or *.* or *.JP*G or *.JP? + if (pattern.startsWith(QLatin1String("*.")) && + (pattern.length() > 2) && + (pattern.indexOf(QLatin1Char('*'), 2) < 0) && (pattern.indexOf(QLatin1Char('?'), 2) < 0)) + { + const QString suffix = pattern.mid(2); + result.append(suffix); + } + } + + return result; +} + + +/*! + * \fn QString QMimeType::preferredSuffix() const; + * Returns the preferred suffix for the MIME type. + */ +QString QMimeType::preferredSuffix() const +{ + const QStringList suffixList = suffixes(); + + return suffixList.isEmpty() ? QString() : suffixList.at(0); +} + + +/*! + * \fn QString QMimeType::filterString() const; + * Returns a filter string usable for a file dialog. + */ +QString QMimeType::filterString() const +{ + QMimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d); + QString filter; + + if (!d->globPatterns.empty()) + { + filter += comment() + QLatin1String(" ("); + for (int i = 0; i < d->globPatterns.size(); ++i) + { + if (i != 0) + { + filter += QLatin1Char(' '); + } + filter += d->globPatterns.at(i); + } + filter += QLatin1Char(')'); + } + + return filter; +} + + +/*! + * \fn bool QMimeType::inherits(const QString &mimeTypeName) const; + * Returns true if this mimetype is \a mimeTypeName, + * or inherits \a mimeTypeName (see parentMimeTypes()), + * or \a mimeTypeName is an alias for this mimetype. + */ +bool QMimeType::inherits(const QString& mimeTypeName) const +{ + if (d->name == mimeTypeName) + { + return true; + } + return QMimeDatabasePrivate::instance()->inherits(d->name, mimeTypeName); +} + + +#undef DBG + +QT_END_NAMESPACE diff --git lib/MimeHandler/NBMimeType.hpp lib/MimeHandler/NBMimeType.hpp new file mode 100644 index 0000000..11f655a --- /dev/null +++ lib/MimeHandler/NBMimeType.hpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once +#ifndef NBMIMETYPE_HPP +#define NBMIMETYPE_HPP + +#include +#include + +class QMimeTypePrivate; +class QFileinfo; +class QStringList; + +class QMimeType +{ +public: + QMimeType(); + QMimeType(const QMimeType &other); + QMimeType &operator=(const QMimeType &other); +#ifdef Q_COMPILER_RVALUE_REFS + QMimeType &operator=(QMimeType &&other) + { + qSwap(d, other.d); + return *this; + } +#endif + void swap(QMimeType &other) + { + qSwap(d, other.d); + } + explicit QMimeType(const QMimeTypePrivate &dd); + ~QMimeType(); + + bool operator==(const QMimeType &other) const; + + inline bool operator!=(const QMimeType &other) const + { + return !operator==(other); + } + + bool isValid() const; + + bool isDefault() const; + + QString name() const; + QString comment() const; + QString genericIconName() const; + QString iconName() const; + QStringList globPatterns() const; + QStringList parentMimeTypes() const; + QStringList allAncestors() const; + QStringList suffixes() const; + QString preferredSuffix() const; + + bool inherits(const QString &mimeTypeName) const; + + QString filterString() const; + +protected: + friend class QMimeTypeParserBase; + friend class MimeTypeMapEntry; + friend class QMimeDatabasePrivate; + friend class QMimeXMLProvider; + friend class QMimeBinaryProvider; + friend class QMimeTypePrivate; + + QExplicitlySharedDataPointer d; +}; + +QT_END_NAMESPACE + +#endif // QMIMETYPE_H diff --git lib/MimeHandler/NBMimeTypeParser.cpp lib/MimeHandler/NBMimeTypeParser.cpp new file mode 100644 index 0000000..18ef32f --- /dev/null +++ lib/MimeHandler/NBMimeTypeParser.cpp @@ -0,0 +1,433 @@ +/**************************************************************************** + ** + ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/ + ** + ** This file is part of the QtCore module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** GNU Lesser General Public License Usage + ** This file may be used under the terms of the GNU Lesser General Public + ** License version 2.1 as published by the Free Software Foundation and + ** appearing in the file LICENSE.LGPL included in the packaging of this + ** file. Please review the following information to ensure the GNU Lesser + ** General Public License version 2.1 requirements will be met: + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU General + ** Public License version 3.0 as published by the Free Software Foundation + ** and appearing in the file LICENSE.GPL included in the packaging of this + ** file. Please review the following information to ensure the GNU General + ** Public License version 3.0 requirements will be met: + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** Other Usage + ** Alternatively, this file may be used in accordance with the terms and + ** conditions contained in a signed written agreement between you and Nokia. + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#define QT_NO_CAST_FROM_ASCII + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// XML tags in MIME files +static const char mimeInfoTagC[] = "mime-info"; +static const char mimeTypeTagC[] = "mime-type"; +static const char mimeTypeAttributeC[] = "type"; +static const char subClassTagC[] = "sub-class-of"; +static const char commentTagC[] = "comment"; +static const char genericIconTagC[] = "generic-icon"; +static const char iconTagC[] = "icon"; +static const char nameAttributeC[] = "name"; +static const char globTagC[] = "glob"; +static const char aliasTagC[] = "alias"; +static const char patternAttributeC[] = "pattern"; +static const char weightAttributeC[] = "weight"; +static const char caseSensitiveAttributeC[] = "case-sensitive"; +static const char localeAttributeC[] = "xml:lang"; + +static const char magicTagC[] = "magic"; +static const char priorityAttributeC[] = "priority"; + +static const char matchTagC[] = "match"; +static const char matchValueAttributeC[] = "value"; +static const char matchTypeAttributeC[] = "type"; +static const char matchOffsetAttributeC[] = "offset"; +static const char matchMaskAttributeC[] = "mask"; + +/*! + * \class QMimeTypeParser + * \internal + * \brief The QMimeTypeParser class parses MIME types, and builds a MIME database hierarchy by adding to QMimeDatabasePrivate. + * + * Populates QMimeDataBase + * + * \sa QMimeDatabase, QMimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern + * \sa QMimeTypeParser + */ + +/*! + * \class QMimeTypeParserBase + * \internal + * \brief The QMimeTypeParserBase class parses for a sequence of in a generic way. + * + * Calls abstract handler function process for QMimeType it finds. + * + * \sa QMimeDatabase, QMimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern + * \sa QMimeTypeParser + */ + +/*! + * \fn virtual bool QMimeTypeParserBase::process(const QMimeType &t, QString *errorMessage) = 0; + * Overwrite to process the sequence of parsed data + */ +QMimeTypeParserBase::ParseState QMimeTypeParserBase::nextState(ParseState currentState, const QStringRef& startElement) +{ + switch (currentState) + { + case ParseBeginning: + if (startElement == QLatin1String(mimeInfoTagC)) + { + return ParseMimeInfo; + } + if (startElement == QLatin1String(mimeTypeTagC)) + { + return ParseMimeType; + } + return ParseError; + + case ParseMimeInfo: + return startElement == QLatin1String(mimeTypeTagC) ? ParseMimeType : ParseError; + + case ParseMimeType: + case ParseComment: + case ParseGenericIcon: + case ParseIcon: + case ParseGlobPattern: + case ParseSubClass: + case ParseAlias: + case ParseOtherMimeTypeSubTag: + case ParseMagicMatchRule: + if (startElement == QLatin1String(mimeTypeTagC)) // Sequence of + { + return ParseMimeType; + } + if (startElement == QLatin1String(commentTagC)) + { + return ParseComment; + } + if (startElement == QLatin1String(genericIconTagC)) + { + return ParseGenericIcon; + } + if (startElement == QLatin1String(iconTagC)) + { + return ParseIcon; + } + if (startElement == QLatin1String(globTagC)) + { + return ParseGlobPattern; + } + if (startElement == QLatin1String(subClassTagC)) + { + return ParseSubClass; + } + if (startElement == QLatin1String(aliasTagC)) + { + return ParseAlias; + } + if (startElement == QLatin1String(magicTagC)) + { + return ParseMagic; + } + if (startElement == QLatin1String(matchTagC)) + { + return ParseMagicMatchRule; + } + return ParseOtherMimeTypeSubTag; + + case ParseMagic: + if (startElement == QLatin1String(matchTagC)) + { + return ParseMagicMatchRule; + } + break; + + case ParseError: + break; + } + return ParseError; +} + + +// Parse int number from an (attribute) string) +static bool parseNumber(const QString& n, int *target, QString *errorMessage) +{ + bool ok; + + *target = n.toInt(&ok); + if (!ok) + { + *errorMessage = QString::fromLatin1("Not a number '%1'.").arg(n); + return false; + } + return true; +} + + +// Evaluate a magic match rule like +// +// +static bool createMagicMatchRule(const QXmlStreamAttributes& atts, + QString *errorMessage, QMimeMagicRule *& rule) +{ + const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString(); + QMimeMagicRule::Type magicType = QMimeMagicRule::type(type.toLatin1()); + + if (magicType == QMimeMagicRule::Invalid) + { + qWarning("%s: match type %s is not supported.", Q_FUNC_INFO, type.toUtf8().constData()); + return true; + } + const QString value = atts.value(QLatin1String(matchValueAttributeC)).toString(); + + if (value.isEmpty()) + { + *errorMessage = QString::fromLatin1("Empty match value detected."); + return false; + } + // Parse for offset as "1" or "1:10" + int startPos, endPos; + const QString offsetS = atts.value(QLatin1String(matchOffsetAttributeC)).toString(); + const int colonIndex = offsetS.indexOf(QLatin1Char(':')); + const QString startPosS = colonIndex == -1 ? offsetS : offsetS.mid(0, colonIndex); + const QString endPosS = colonIndex == -1 ? offsetS : offsetS.mid(colonIndex + 1); + + if (!parseNumber(startPosS, &startPos, errorMessage) || !parseNumber(endPosS, &endPos, errorMessage)) + { + return false; + } + const QString mask = atts.value(QLatin1String(matchMaskAttributeC)).toString(); + + rule = new QMimeMagicRule(magicType, value.toUtf8(), startPos, endPos, mask.toLatin1()); + + return true; +} + + +bool QMimeTypeParserBase::parse(QIODevice *dev, const QString& fileName, QString *errorMessage) +{ + QMimeTypePrivate data; + int priority = 50; + QStack currentRules; // stack for the nesting of rules + QList rules; // toplevel rules + QXmlStreamReader reader(dev); + ParseState ps = ParseBeginning; + QXmlStreamAttributes atts; + + while (!reader.atEnd()) + { + switch (reader.readNext()) + { + case QXmlStreamReader::StartElement: + ps = nextState(ps, reader.name()); + atts = reader.attributes(); + switch (ps) + { + case ParseMimeType: + { // start parsing a MIME type name + const QString name = atts.value(QLatin1String(mimeTypeAttributeC)).toString(); + if (name.isEmpty()) + { + reader.raiseError(QString::fromLatin1("Missing '%1'-attribute").arg(QString::fromLatin1(mimeTypeAttributeC))); + } + else + { + data.name = name; + } + } + break; + + case ParseGenericIcon: + data.genericIconName = atts.value(QLatin1String(nameAttributeC)).toString(); + break; + + case ParseIcon: + data.iconName = atts.value(QLatin1String(nameAttributeC)).toString(); + break; + + case ParseGlobPattern: + { + const QString pattern = atts.value(QLatin1String(patternAttributeC)).toString(); + unsigned weight = atts.value(QLatin1String(weightAttributeC)).toString().toInt(); + const bool caseSensitive = atts.value(QLatin1String(caseSensitiveAttributeC)).toString() == QLatin1String("true"); + + if (weight == 0) + { + weight = QMimeGlobPattern::DefaultWeight; + } + + Q_ASSERT(!data.name.isEmpty()); + const QMimeGlobPattern glob(pattern, data.name, weight, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + if (!process(glob, errorMessage)) // for actual glob matching + { + return false; + } + data.addGlobPattern(pattern); // just for QMimeType::globPatterns() + } + break; + + case ParseSubClass: + { + const QString inheritsFrom = atts.value(QLatin1String(mimeTypeAttributeC)).toString(); + if (!inheritsFrom.isEmpty()) + { + processParent(data.name, inheritsFrom); + } + } + break; + + case ParseComment: + { + // comments have locale attributes. We want the default, English one + QString locale = atts.value(QLatin1String(localeAttributeC)).toString(); + const QString comment = reader.readElementText(); + if (locale.isEmpty()) + { + locale = QString::fromLatin1("en_US"); + } + data.localeComments.insert(locale, comment); + } + break; + + case ParseAlias: + { + const QString alias = atts.value(QLatin1String(mimeTypeAttributeC)).toString(); + if (!alias.isEmpty()) + { + processAlias(alias, data.name); + } + } + break; + + case ParseMagic: + { + priority = 50; + const QString priorityS = atts.value(QLatin1String(priorityAttributeC)).toString(); + if (!priorityS.isEmpty()) + { + if (!parseNumber(priorityS, &priority, errorMessage)) + { + return false; + } + } + currentRules.clear(); + //qDebug() << "MAGIC start for mimetype" << data.name; + } + break; + + case ParseMagicMatchRule: + { + QMimeMagicRule *rule = 0; + if (!createMagicMatchRule(atts, errorMessage, rule)) + { + return false; + } + QList *ruleList; + if (currentRules.isEmpty()) + { + ruleList = &rules; + } + else // nest this rule into the proper parent + { + ruleList = ¤tRules.top()->m_subMatches; + } + ruleList->append(*rule); + //qDebug() << " MATCH added. Stack size was" << currentRules.size(); + currentRules.push(&ruleList->last()); + delete rule; + break; + } + + case ParseError: + reader.raiseError(QString::fromLatin1("Unexpected element <%1>"). + arg(reader.name().toString())); + break; + + default: + break; + } + break; + + // continue switch QXmlStreamReader::Token... + case QXmlStreamReader::EndElement: // Finished element + { + const QStringRef elementName = reader.name(); + if (elementName == QLatin1String(mimeTypeTagC)) + { + if (!process(QMimeType(data), errorMessage)) + { + return false; + } + data.clear(); + } + else if (elementName == QLatin1String(matchTagC)) + { + // Closing a tag, pop stack + currentRules.pop(); + //qDebug() << " MATCH closed. Stack size is now" << currentRules.size(); + } + else if (elementName == QLatin1String(magicTagC)) + { + //qDebug() << "MAGIC ended, we got" << rules.count() << "rules, with prio" << priority; + // Finished a sequence + QMimeMagicRuleMatcher ruleMatcher(data.name, priority); + ruleMatcher.addRules(rules); + processMagicMatcher(ruleMatcher); + rules.clear(); + } + break; + } + + default: + break; + } + } + + if (reader.hasError()) + { + if (errorMessage) + { + *errorMessage = QString::fromLatin1("An error has been encountered at line %1 of %2: %3:").arg(reader.lineNumber()).arg(fileName, reader.errorString()); + } + return false; + } + + return true; +} + + +QT_END_NAMESPACE diff --git lib/MimeHandler/NBMimeTypeParser_p.hpp lib/MimeHandler/NBMimeTypeParser_p.hpp new file mode 100644 index 0000000..e4ad366 --- /dev/null +++ lib/MimeHandler/NBMimeTypeParser_p.hpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef MIMETYPEPARSER_P_H +#define MIMETYPEPARSER_P_H + +#include +#include + +class QIODevice; + +class QMimeTypeParserBase +{ + Q_DISABLE_COPY(QMimeTypeParserBase) + +public: + QMimeTypeParserBase() {} + virtual ~QMimeTypeParserBase() {} + + bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage); + +protected: + virtual bool process(const QMimeType &t, QString *errorMessage) = 0; + virtual bool process(const QMimeGlobPattern &t, QString *errorMessage) = 0; + virtual void processParent(const QString &child, const QString &parent) = 0; + virtual void processAlias(const QString &alias, const QString &name) = 0; + virtual void processMagicMatcher(const QMimeMagicRuleMatcher &matcher) = 0; + +private: + enum ParseState { + ParseBeginning, + ParseMimeInfo, + ParseMimeType, + ParseComment, + ParseGenericIcon, + ParseIcon, + ParseGlobPattern, + ParseSubClass, + ParseAlias, + ParseMagic, + ParseMagicMatchRule, + ParseOtherMimeTypeSubTag, + ParseError + }; + + static ParseState nextState(ParseState currentState, const QStringRef &startElement); +}; + + +class QMimeTypeParser : public QMimeTypeParserBase +{ +public: + explicit QMimeTypeParser(QMimeXMLProvider &provider) : m_provider(provider) {} + +protected: + inline bool process(const QMimeType &t, QString *) + { m_provider.addMimeType(t); return true; } + + inline bool process(const QMimeGlobPattern &glob, QString *) + { m_provider.addGlobPattern(glob); return true; } + + inline void processParent(const QString &child, const QString &parent) + { m_provider.addParent(child, parent); } + + inline void processAlias(const QString &alias, const QString &name) + { m_provider.addAlias(alias, name); } + + inline void processMagicMatcher(const QMimeMagicRuleMatcher &matcher) + { m_provider.addMagicMatcher(matcher); } + +private: + QMimeXMLProvider &m_provider; +}; + +QT_END_NAMESPACE + +#endif // MIMETYPEPARSER_P_H diff --git lib/MimeHandler/NBMimeType_p.hpp lib/MimeHandler/NBMimeType_p.hpp new file mode 100644 index 0000000..6f45530 --- /dev/null +++ lib/MimeHandler/NBMimeType_p.hpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMIMETYPE_P_H +#define QMIMETYPE_P_H + +#include + +#include +#include + +class Q_AUTOTEST_EXPORT QMimeTypePrivate : public QSharedData +{ +public: + typedef QHash LocaleHash; + + QMimeTypePrivate(); + explicit QMimeTypePrivate(const QMimeType &other); + + void clear(); + + bool operator==(const QMimeTypePrivate &other) const; + + void addGlobPattern(const QString &pattern); + + QString name; + LocaleHash localeComments; + QString genericIconName; + QString iconName; + QStringList globPatterns; +}; + +QT_END_NAMESPACE + +#define QMIMETYPE_BUILDER \ + QT_BEGIN_NAMESPACE \ + static QMimeType buildQMimeType ( \ + const QString &name, \ + const QString &genericIconName, \ + const QString &iconName, \ + const QStringList &globPatterns \ + ) \ + { \ + QMimeTypePrivate qMimeTypeData; \ + qMimeTypeData.name = name; \ + qMimeTypeData.genericIconName = genericIconName; \ + qMimeTypeData.iconName = iconName; \ + qMimeTypeData.globPatterns = globPatterns; \ + return QMimeType(qMimeTypeData); \ + } \ + QT_END_NAMESPACE + +#ifdef Q_COMPILER_RVALUE_REFS +#define QMIMETYPE_BUILDER_FROM_RVALUE_REFS \ + QT_BEGIN_NAMESPACE \ + static QMimeType buildQMimeType ( \ + QString &&name, \ + QString &&genericIconName, \ + QString &&iconName, \ + QStringList &&globPatterns \ + ) \ + { \ + QMimeTypePrivate qMimeTypeData; \ + qMimeTypeData.name = std::move(name); \ + qMimeTypeData.genericIconName = std::move(genericIconName); \ + qMimeTypeData.iconName = std::move(iconName); \ + qMimeTypeData.globPatterns = std::move(globPatterns); \ + return QMimeType(qMimeTypeData); \ + } \ + QT_END_NAMESPACE +#endif + +#endif // QMIMETYPE_P_H diff --git lib/StandardPaths/NBStandardPaths.cpp lib/StandardPaths/NBStandardPaths.cpp new file mode 100644 index 0000000..c26cd3b --- /dev/null +++ lib/StandardPaths/NBStandardPaths.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** + ** + ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the QtGui module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** GNU Lesser General Public License Usage + ** This file may be used under the terms of the GNU Lesser General Public + ** License version 2.1 as published by the Free Software Foundation and + ** appearing in the file LICENSE.LGPL included in the packaging of this + ** file. Please review the following information to ensure the GNU Lesser + ** General Public License version 2.1 requirements will be met: + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU General + ** Public License version 3.0 as published by the Free Software Foundation + ** and appearing in the file LICENSE.GPL included in the packaging of this + ** file. Please review the following information to ensure the GNU General + ** Public License version 3.0 requirements will be met: + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** Other Usage + ** Alternatively, this file may be used in accordance with the terms and + ** conditions contained in a signed written agreement between you and Nokia. + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#ifndef QT_NO_STANDARDPATHS + +/*! + * \class QStandardPaths + * \brief The QStandardPaths class provides methods for accessing standard paths. + * \since 5.0 + * + * This class contains functions to query standard locations on the local + * filesystem, for common tasks such as user-specific directories or system-wide + * configuration directories. + */ + +/*! + * \enum QStandardPaths::StandardLocation + * + * This enum describes the different locations that can be queried using + * methods such as QStandardPaths::writableLocation, QStandardPaths::standardLocations, + * and QStandardPaths::displayName. + * + * \value DesktopLocation Returns the user's desktop directory. + * \value DocumentsLocation Returns the user's document. + * \value FontsLocation Returns the user's fonts. + * \value ApplicationsLocation Returns the user's applications. + * \value MusicLocation Returns the users music. + * \value MoviesLocation Returns the user's movies. + * \value PicturesLocation Returns the user's pictures. + * \value TempLocation Returns the system's temporary directory. + * \value HomeLocation Returns the user's home directory. + * \value DataLocation Returns a directory location where persistent + * application data can be stored. QCoreApplication::organizationName + * and QCoreApplication::applicationName are appended to the directory location + * returned for GenericDataLocation. + * \value CacheLocation Returns a directory location where user-specific + * non-essential (cached) data should be written. + * \value GenericDataLocation Returns a directory location where persistent + * data shared across applications can be stored. + * \value RuntimeLocation Returns a directory location where runtime communication + * files should be written. For instance unix local sockets. + * \value ConfigLocation Returns a directory location where user-specific + * configuration files should be written. + * + * + * \sa writableLocation(), standardLocations(), displayName(), locate(), locateAll() + */ + +/*! + * \fn QString QStandardPaths::writableLocation(StandardLocation type) + * + * Returns the directory where files of \a type should be written to, or an empty string + * if the location cannot be determined. + * + * \note The storage location returned can be a directory that does not exist; i.e., it + * may need to be created by the system or the user. + * + * \note On Symbian OS, ApplicationsLocation always point /sys/bin folder on the same drive + * with executable. FontsLocation always points to folder on ROM drive. Symbian OS does not + * have desktop concept, DesktopLocation returns same path as DocumentsLocation. + * Rest of the standard locations point to folder on same drive with executable, except + * that if executable is in ROM the folder from C drive is returned. + */ + + +/*! + * \fn QStringList QStandardPaths::standardLocations(StandardLocation type) + * + * Returns all the directories where files of \a type belong. + * + * Much like the PATH variable, it returns the directories in order of priority, + * starting with the user-specific writableLocation() for the \a type. + */ + +/*! + * \enum QStandardPaths::LocateOption + * + * This enum describes the different flags that can be used for + * controlling the behavior of QStandardPaths::locate and + * QStandardPaths::locateAll. + * + * \value LocateFile return only files + * \value LocateDirectory return only directories + */ +static bool existsAsSpecified(const QString& path, QStandardPaths::LocateOptions options) +{ + if (options & QStandardPaths::LocateDirectory) + { + return QDir(path).exists(); + } + return QFileInfo(path).isFile(); +} + + +/*! + * Tries to find a file or directory called \a fileName in the standard locations + * for \a type. + * + * The full path to the first file or directory (depending on \a options) found is returned. + * If no such file or directory can be found, an empty string is returned. + */ +QString QStandardPaths::locate(StandardLocation type, const QString& fileName, LocateOptions options) +{ + const QStringList dirs = standardLocations(type); + + for (QStringList::const_iterator dir = dirs.constBegin(); dir != dirs.constEnd(); ++dir) + { + const QString path = *dir + QLatin1Char('/') + fileName; + if (existsAsSpecified(path, options)) + { + return path; + } + } + return QString(); +} + + +/*! + * Tries to find all files or directories called \a fileName in the standard locations + * for \a type. + * + * The \a options flag allows to specify whether to look for files or directories. + * + * Returns the list of all the files that were found. + */ +QStringList QStandardPaths::locateAll(StandardLocation type, const QString& fileName, LocateOptions options) +{ + const QStringList dirs = standardLocations(type); + QStringList result; + + for (QStringList::const_iterator dir = dirs.constBegin(); dir != dirs.constEnd(); ++dir) + { + const QString path = *dir + QLatin1Char('/') + fileName; + if (existsAsSpecified(path, options)) + { + result.append(path); + } + } + return result; +} + + +#ifdef Q_OS_WIN +static QStringList executableExtensions() +{ + QStringList ret = QString::fromLocal8Bit(qgetenv("PATHEXT")).split(QLatin1Char(';')); + + if (!ret.contains(QLatin1String(".exe"), Qt::CaseInsensitive)) + { + // If %PATHEXT% does not contain .exe, it is either empty, malformed, or distorted in ways that we cannot support, anyway. + ret.clear(); + ret << QLatin1String(".exe") + << QLatin1String(".com") + << QLatin1String(".bat") + << QLatin1String(".cmd"); + } + return ret; +} + + +#endif + +static QString checkExecutable(const QString& path) +{ + const QFileInfo info(path); + + if (info.isBundle()) + { + return info.bundleName(); + } + if (info.isFile() && info.isExecutable()) + { + return QDir::cleanPath(path); + } + return QString(); +} + + +/*! + * Finds the executable named \a executableName in the paths specified by \a paths, + * or the system paths if \a paths is empty. + * + * On most operating systems the system path is determined by the PATH environment variable. + * + * The directories where to search for the executable can be set in the \a paths argument. + * To search in both your own paths and the system paths, call findExecutable twice, once with + * \a paths set and once with \a paths empty. + * + * Symlinks are not resolved, in order to preserve behavior for the case of executables + * whose behavior depends on the name they are invoked with. + * + * \note On Windows, the usual executable extensions (from the PATHEXT environment variable) + * are automatically appended, so that for instance findExecutable("foo") will find foo.exe + * or foo.bat if present. + * + * Returns the absolute file path to the executable, or an empty string if not found. + */ +QString QStandardPaths::findExecutable(const QString& executableName, const QStringList& paths) +{ + QStringList searchPaths = paths; + + if (paths.isEmpty()) + { + QByteArray pEnv = qgetenv("PATH"); +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + const QLatin1Char pathSep(';'); +#else + const QLatin1Char pathSep(':'); +#endif + searchPaths = QString::fromLocal8Bit(pEnv.constData()).split(pathSep, QString::SkipEmptyParts); + } + + if (QFileInfo(executableName).isAbsolute()) + { + return checkExecutable(executableName); + } + + QDir currentDir = QDir::current(); + QString absPath; +#ifdef Q_OS_WIN + static QStringList executable_extensions = executableExtensions(); +#endif + + for (QStringList::const_iterator p = searchPaths.constBegin(); p != searchPaths.constEnd(); ++p) + { + const QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + executableName); +#ifdef Q_OS_WIN + const QString extension = QLatin1Char('.') + QFileInfo(executableName).suffix(); + if (!executable_extensions.contains(extension, Qt::CaseInsensitive)) + { + foreach(const QString& extension, executable_extensions) + { + absPath = checkExecutable(candidate + extension.toLower()); + if (!absPath.isEmpty()) + { + break; + } + } + } +#endif + absPath = checkExecutable(candidate); + if (!absPath.isEmpty()) + { + break; + } + } + return absPath; +} + + +/*! + * \fn QString QStandardPaths::displayName(StandardLocation type) + * + * Returns a localized display name for the given location \a type or + * an empty QString if no relevant location can be found. + */ + +QT_END_NAMESPACE + +#endif // QT_NO_STANDARDPATHS diff --git lib/StandardPaths/NBStandardPaths.hpp lib/StandardPaths/NBStandardPaths.hpp new file mode 100644 index 0000000..efff304 --- /dev/null +++ lib/StandardPaths/NBStandardPaths.hpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTANDARDPATHS_H +#define QSTANDARDPATHS_H + +#include +QT_BEGIN_HEADER + +QT_MODULE(Core) + +#ifndef QT_NO_STANDARDPATHS + +class QStringList; + +class QStandardPaths +{ +public: + // Do not re-order, must match QDesktopServices + enum StandardLocation { + DesktopLocation, + DocumentsLocation, + FontsLocation, + ApplicationsLocation, + MusicLocation, + MoviesLocation, + PicturesLocation, + TempLocation, + HomeLocation, + DataLocation, + CacheLocation, + GenericDataLocation, + RuntimeLocation, + ConfigLocation + }; + + static QString writableLocation(StandardLocation type); + static QStringList standardLocations(StandardLocation type); + + enum LocateOption { + LocateFile = 0x0, + LocateDirectory = 0x1 + }; + Q_DECLARE_FLAGS(LocateOptions, LocateOption) + + static QString locate(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile); + static QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options = LocateFile); + static QString displayName(StandardLocation type); + + static QString findExecutable(const QString &executableName, const QStringList &paths = QStringList()); + +private: + // prevent construction + QStandardPaths(); + ~QStandardPaths(); +}; + +#endif // QT_NO_STANDARDPATHS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSTANDARDPATHS_H diff --git lib/StandardPaths/NBStandardPaths_unix.cpp lib/StandardPaths/NBStandardPaths_unix.cpp new file mode 100644 index 0000000..2a7c1cf --- /dev/null +++ lib/StandardPaths/NBStandardPaths_unix.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** + ** + ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the QtGui module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** GNU Lesser General Public License Usage + ** This file may be used under the terms of the GNU Lesser General Public + ** License version 2.1 as published by the Free Software Foundation and + ** appearing in the file LICENSE.LGPL included in the packaging of this + ** file. Please review the following information to ensure the GNU Lesser + ** General Public License version 2.1 requirements will be met: + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU General + ** Public License version 3.0 as published by the Free Software Foundation + ** and appearing in the file LICENSE.GPL included in the packaging of this + ** file. Please review the following information to ensure the GNU General + ** Public License version 3.0 requirements will be met: + ** http://www.gnu.org/copyleft/gpl.html. + ** + ** Other Usage + ** Alternatively, this file may be used in accordance with the terms and + ** conditions contained in a signed written agreement between you and Nokia. + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION <= QT_VERSION_CHECK(4, 7, 0) // i do not sure but for qt 4.7 and 4.6 we need this include +#include +#endif +// LOCAL HACK #include +#include +#include +#include + +#ifndef QT_NO_STANDARDPATHS + +QString QStandardPaths::writableLocation(StandardLocation type) +{ + switch (type) + { + case HomeLocation: + return QDir::homePath(); + + case TempLocation: + return QDir::tempPath(); + + case CacheLocation: + { + // http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html + QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME")); + if (xdgCacheHome.isEmpty()) + { + xdgCacheHome = QDir::homePath() + QLatin1String("/.cache"); + } + if (!QCoreApplication::organizationName().isEmpty()) + { + xdgCacheHome += QLatin1Char('/') + QCoreApplication::organizationName(); + } + if (!QCoreApplication::applicationName().isEmpty()) + { + xdgCacheHome += QLatin1Char('/') + QCoreApplication::applicationName(); + } + return xdgCacheHome; + } + + case DataLocation: + case GenericDataLocation: + { + QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME")); + if (xdgDataHome.isEmpty()) + { + xdgDataHome = QDir::homePath() + QLatin1String("/.local/share"); + } + if (type == QStandardPaths::DataLocation) + { + if (!QCoreApplication::organizationName().isEmpty()) + { + xdgDataHome += QLatin1Char('/') + QCoreApplication::organizationName(); + } + if (!QCoreApplication::applicationName().isEmpty()) + { + xdgDataHome += QLatin1Char('/') + QCoreApplication::applicationName(); + } + } + return xdgDataHome; + } + + case ConfigLocation: + { + // http://standards.freedesktop.org/basedir-spec/latest/ + QString xdgConfigHome = QFile::decodeName(qgetenv("XDG_CONFIG_HOME")); + if (xdgConfigHome.isEmpty()) + { + xdgConfigHome = QDir::homePath() + QLatin1String("/.config"); + } + return xdgConfigHome; + } + + case RuntimeLocation: + { + const uid_t myUid = geteuid(); + // http://standards.freedesktop.org/basedir-spec/latest/ + QString xdgRuntimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR")); + if (xdgRuntimeDir.isEmpty()) + { + const QString userName = QString(); // LOCAL HACK QFileSystemEngine::resolveUserName(myUid); + xdgRuntimeDir = QDir::tempPath() + QLatin1String("/runtime-") + userName; + QDir dir(xdgRuntimeDir); + if (!dir.exists()) + { + if (!QDir().mkdir(xdgRuntimeDir)) + { + qWarning("QStandardPaths: error creating runtime directory %s: %s", qPrintable(xdgRuntimeDir), qPrintable(qt_error_string(errno))); + return QString(); + } + } + } + // "The directory MUST be owned by the user" + QFileInfo fileInfo(xdgRuntimeDir); + if (fileInfo.ownerId() != myUid) + { + qWarning("QStandardPaths: wrong ownership on runtime directory %s, %d instead of %d", qPrintable(xdgRuntimeDir), + fileInfo.ownerId(), myUid); + return QString(); + } + // "and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700." + QFile file(xdgRuntimeDir); + const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser; + if ((file.permissions() != wantedPerms) && !file.setPermissions(wantedPerms)) + { + qWarning("QStandardPaths: wrong permissions on runtime directory %s", qPrintable(xdgRuntimeDir)); + return QString(); + } + return xdgRuntimeDir; + } + + default: + break; + } + + // http://www.freedesktop.org/wiki/Software/xdg-user-dirs + QString xdgConfigHome = QLatin1String(qgetenv("XDG_CONFIG_HOME")); + + if (xdgConfigHome.isEmpty()) + { + xdgConfigHome = QDir::homePath() + QLatin1String("/.config"); + } + QFile file(xdgConfigHome + QLatin1String("/user-dirs.dirs")); + + if (file.exists() && file.open(QIODevice::ReadOnly)) + { + QHash lines; + QTextStream stream(&file); + // Only look for lines like: XDG_DESKTOP_DIR="$HOME/Desktop" + QRegExp exp(QLatin1String("^XDG_(.*)_DIR=(.*)$")); + while (!stream.atEnd()) + { + const QString line = stream.readLine(); + if (exp.indexIn(line) != -1) + { + QStringList lst = exp.capturedTexts(); + QString key = lst.at(1); + QString value = lst.at(2); + if ((value.length() > 2) && + value.startsWith(QLatin1Char('\"')) && + value.endsWith(QLatin1Char('\"'))) + { + value = value.mid(1, value.length() - 2); + } + // Store the key and value: "DESKTOP", "$HOME/Desktop" + lines[key] = value; + } + } + + QString key; + switch (type) + { + case DesktopLocation: + key = QLatin1String("DESKTOP"); + break; + + case DocumentsLocation: + key = QLatin1String("DOCUMENTS"); + break; + + case PicturesLocation: + key = QLatin1String("PICTURES"); + break; + + case MusicLocation: + key = QLatin1String("MUSIC"); + break; + + case MoviesLocation: + key = QLatin1String("VIDEOS"); + break; + + default: + break; + } + if (!key.isEmpty() && lines.contains(key)) + { + QString value = lines[key]; + // value can start with $HOME + if (value.startsWith(QLatin1String("$HOME"))) + { + value = QDir::homePath() + value.mid(5); + } + return value; + } + } + + QString path; + + switch (type) + { + case DesktopLocation: + path = QDir::homePath() + QLatin1String("/Desktop"); + break; + + case DocumentsLocation: + path = QDir::homePath() + QLatin1String("/Documents"); + break; + + case PicturesLocation: + path = QDir::homePath() + QLatin1String("/Pictures"); + break; + + case FontsLocation: + path = QDir::homePath() + QLatin1String("/.fonts"); + break; + + case MusicLocation: + path = QDir::homePath() + QLatin1String("/Music"); + break; + + case MoviesLocation: + path = QDir::homePath() + QLatin1String("/Videos"); + break; + + case ApplicationsLocation: + path = writableLocation(GenericDataLocation) + QLatin1String("/applications"); + break; + + default: + break; + } + + return path; +} + + +QStringList QStandardPaths::standardLocations(StandardLocation type) +{ + QStringList dirs; + + if (type == ConfigLocation) + { + // http://standards.freedesktop.org/basedir-spec/latest/ + QString xdgConfigDirs = QFile::decodeName(qgetenv("XDG_CONFIG_DIRS")); + if (xdgConfigDirs.isEmpty()) + { + dirs.append(QString::fromLatin1("/etc/xdg")); + } + else + { + dirs = xdgConfigDirs.split(QLatin1Char(':')); + } + } + else if (type == GenericDataLocation) + { + // http://standards.freedesktop.org/basedir-spec/latest/ + QString xdgConfigDirs = QFile::decodeName(qgetenv("XDG_DATA_DIRS")); + if (xdgConfigDirs.isEmpty()) + { + dirs.append(QString::fromLatin1("/usr/local/share")); + dirs.append(QString::fromLatin1("/usr/share")); + } + else + { + dirs = xdgConfigDirs.split(QLatin1Char(':')); + } + } + const QString localDir = writableLocation(type); + + dirs.prepend(localDir); + return dirs; +} + + +QString QStandardPaths::displayName(StandardLocation type) +{ + Q_UNUSED(type); + return QString(); +} + + +QT_END_NAMESPACE + +#endif // QT_NO_STANDARDPATHS diff --git lib/libarchiveqt.h lib/libarchiveqt.h index d5188af..f374e73 100644 --- lib/libarchiveqt.h +++ lib/libarchiveqt.h @@ -1,19 +1,27 @@ -/** - * Copyright 2018-2023 Britanicus - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - **/ +/* + * + * Copyright 2018 Britanicus + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ #pragma once @@ -26,130 +34,129 @@ class QMimeType; -typedef struct { - /* Name of the entry */ - QString name; +typedef struct +{ + /* Name of the entry */ + QString name; - /* Size of the entry */ - quint64 size; + /* Size of the entry */ + quint64 size; - /* Type of the entry */ - int type; + /* Type of the entry */ + int type; - /* Stat equivalent */ - struct stat info; + /* Stat equivalent */ + struct stat info; } ArchiveEntry; typedef QList ArchiveEntries; class LibArchiveQt : public QThread { - Q_OBJECT + Q_OBJECT - public: - /* Used with updateInputFiles, this helps libarchive to choose how to handle paths */ - enum InputFileMode { - AbsolutePath = 0x703857, // Use absolute file paths - Discouraged - RelativeToRoot, // All paths will be relative to '/' - Useful for packaging of - // installed files - RelativeToHome, // Files will have paths relative to home folder - good for saving - // config files - RelativeToCurrent, // The paths will be relative to the current path - Useful for - // archiving files in current dir [Default] - RelativeToWorkDir, // Set archive paths of the file relative to @src - @src should be - // set before calling updateInputFiles - CommonRelativePath, // Added files will have paths relative to path common to all files - // - Costly for large number of files - }; +public: + /* Used with updateInputFiles, this helps libarchive to choose how to handle paths */ + enum InputFileMode + { + AbsolutePath = 0x703857, // Use absolute file paths - Discouraged + RelativeToRoot, // All paths will be relative to '/' - Useful for packaging of installed files + RelativeToHome, // Files will have paths relative to home folder - good for saving config files + RelativeToCurrent, // The paths will be relative to the current path - Useful for archiving files in current dir [Default] + RelativeToWorkDir, // Set archive paths of the file relative to @src - @src should be set before calling updateInputFiles + CommonRelativePath, // Added files will have paths relative to path common to all files - Costly for large number of files + }; - LibArchiveQt( QString ); + LibArchiveQt(QString); - // Convenience Functions - void updateInputFiles( QStringList, LibArchiveQt::InputFileMode inMode = LibArchiveQt::RelativeToCurrent ); - void setWorkingDir( QString ); - void setDestination( QString ); - void waitForFinished(); + // Convenience Functions + void updateInputFiles(QStringList, LibArchiveQt::InputFileMode inMode = LibArchiveQt::RelativeToCurrent); + void setWorkingDir(QString); + void setDestination(QString); + void waitForFinished(); - /* Create an archive */ - void createArchive(); + /* Create an archive */ + void createArchive(); - /* Extract the archive */ - void extractArchive(); + /* Extract the archive */ + void extractArchive(); - /* Extract a named member of the archive */ - void extractMember( QString ); + /* Extract a named member of the archive */ + void extractMember(QString); - /* List the contetns of the archive */ - ArchiveEntries listArchive(); + /* List the contetns of the archive */ + ArchiveEntries listArchive(); - /* Exit status */ - int exitStatus(); + /* Exit status */ + int exitStatus(); - /* Convenience functions */ - static QString suffix( QString name ); - static QStringList supportedFormats(); + /* Convenience functions */ + static QString suffix(QString name); + static QStringList supportedFormats(); - private: - enum Mode { - None = 0xF650E7, - Single, - Container - }; +private: + enum Mode + { + None = 0xF650E7, + Single, + Container + }; - enum Job { - NoJob = 0x25CEE9, - CreateArchive, - ExtractArchive, - ExtractMember, - ListArchive - }; + enum Job + { + NoJob = 0x25CEE9, + CreateArchive, + ExtractArchive, + ExtractMember, + ListArchive + }; - /* Internal worker for copying data */ - int copyData( struct archive *ar, struct archive *aw ); + /* Internal worker for copying data */ + int copyData(struct archive *ar, struct archive *aw); - /* Set the archive filter format based on extensions */ - void setFilterFormat( QMimeType mType ); + /* Set the archive filter format based on extensions */ + void setFilterFormat(QMimeType mType); - /* Create an archive - Internal Worker */ - bool doCreateArchive(); + /* Create an archive - Internal Worker */ + bool doCreateArchive(); - /* Extract the archive - Internal Worker */ - bool doExtractArchive(); + /* Extract the archive - Internal Worker */ + bool doExtractArchive(); - /* Extract a named member of the archive - Internal Worker */ - bool doExtractMember( QString ); + /* Extract a named member of the archive - Internal Worker */ + bool doExtractMember(QString); - int mArchiveFilter; - int mArchiveFormat; + int mArchiveFilter; + int mArchiveFormat; - QString archiveName; + QString archiveName; - QHash inputList; - QString dest; - QString src; + QHash inputList; + QString dest; + QString src; - ArchiveEntries memberList; - bool readDone; - int archiveType; + ArchiveEntries memberList; + bool readDone; + int archiveType; - /* What job are we doing? */ - int mJob; + /* What job are we doing? */ + int mJob; - /* Is the job running? */ - bool isRunning; + /* Is the job running? */ + bool isRunning; - /* Exit status */ - int mExitStatus; + /* Exit status */ + int mExitStatus; - /* Member to be extracted */ - QString extractedMember; + /* Member to be extracted */ + QString extractedMember; - protected: - void run(); +protected: + void run(); - Q_SIGNALS: - void jobComplete(); - void jobFailed(); +Q_SIGNALS: + void jobComplete(); + void jobFailed(); - /* Progress is always in percentage */ - void progress( int ); + /* Progress is always in percentage */ + void progress(int); }; diff --git lib/lz4dec.c lib/lz4dec.c index b11d6ba..6685ba8 100644 --- lib/lz4dec.c +++ lib/lz4dec.c @@ -21,8 +21,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// This program is a shorter, more readable, albeit slower re-implementation of lz4cat ( -// https://github.com/Cyan4973/xxHash ) +// This program is a shorter, more readable, albeit slower re-implementation of lz4cat ( https://github.com/Cyan4973/xxHash ) // compile: gcc smallz4cat.c -O3 -o smallz4cat -Wall -pedantic -std=c99 -s // The static 8k binary was compiled using Clang and dietlibc (see https://www.fefe.de/dietlibc/ ) @@ -34,7 +33,7 @@ // Replace getByteFromIn() and sendToOut() by your own code if you need in-memory LZ4 decompression. // Corrupted data causes a call to error(). -#define READ_BUFFER_SIZE 4 * 1024 +#define READ_BUFFER_SIZE 4*1024 #include // uint32_t #include // stdin/stdout/stderr, fopen, ... @@ -44,296 +43,273 @@ #include "lz4dec.h" // error handler -void error( const char *msg ) { - // smaller static binary than fprintf(stderr, "ERROR: %s\n", msg); - fputs( "ERROR: ", stderr ); - fputs( msg, stderr ); - fputs( "\n", stderr ); - exit( 1 ); +void error(const char* msg) { + // smaller static binary than fprintf(stderr, "ERROR: %s\n", msg); + fputs("ERROR: ", stderr); + fputs(msg, stderr); + fputs("\n", stderr); + exit( 1); } - static unsigned char getByte( FILE *in ) { - // modify buffer size as you like ... for most use cases, bigger buffer aren't faster anymore - and even - // reducing to 1 byte works ! - static unsigned char readBuffer[ READ_BUFFER_SIZE ]; - static size_t pos = 0; - static size_t available = 0; - - // refill buffer - if ( pos == available ) { - pos = 0; - available = fread( readBuffer, 1, READ_BUFFER_SIZE, in ); - - if ( available == 0 ) { - error( "out of data" ); - } - } - - // return a byte - return readBuffer[ pos++ ]; + // modify buffer size as you like ... for most use cases, bigger buffer aren't faster anymore - and even reducing to 1 byte works ! + static unsigned char readBuffer[READ_BUFFER_SIZE]; + static size_t pos = 0; + static size_t available = 0; + + // refill buffer + if (pos == available) + { + pos = 0; + available = fread(readBuffer, 1, READ_BUFFER_SIZE, in); + if (available == 0) + error("out of data"); + } + + // return a byte + return readBuffer[pos++]; } - -static void sendBytes( const unsigned char *data, unsigned int numBytes, FILE *out ) { - if ( (data != NULL) && (numBytes > 0) ) { - fwrite( data, 1, numBytes, out ); - } +static void sendBytes(const unsigned char* data, unsigned int numBytes, FILE *out) { + if (data != NULL && numBytes > 0) + fwrite(data, 1, numBytes, out); } - -void unlz4( const char *ifn, const char *ofn, const char *dictionary ) { - FILE *inpFp = fopen( ifn, "rb" ); - FILE *outFp = fopen( ofn, "wb" ); - - // signature - unsigned char signature1 = getByte( inpFp ); - unsigned char signature2 = getByte( inpFp ); - unsigned char signature3 = getByte( inpFp ); - unsigned char signature4 = getByte( inpFp ); - uint32_t signature = (signature4 << 24) | (signature3 << 16) | (signature2 << 8) | signature1; - int isModern = (signature == 0x184D2204); - int isLegacy = (signature == 0x184C2102); - - if ( !isModern && !isLegacy ) { - error( "invalid signature" ); - } - - unsigned char hasBlockChecksum = 0; - unsigned char hasContentSize = 0; - unsigned char hasContentChecksum = 0; - unsigned char hasDictionaryID = 0; - - if ( isModern ) { - // flags - unsigned char flags = getByte( inpFp ); - hasBlockChecksum = flags & 16; - hasContentSize = flags & 8; - hasContentChecksum = flags & 4; - hasDictionaryID = flags & 1; - - // only version 1 file format - unsigned char version = flags >> 6; - - if ( version != 1 ) { - error( "only LZ4 file format version 1 supported" ); - } - - // ignore blocksize - getByte( inpFp ); - - if ( hasContentSize ) { - // ignore, skip 8 bytes - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - } - - if ( hasDictionaryID ) { - // ignore, skip 4 bytes - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - } - - // ignore header checksum (xxhash32 of everything up this point & 0xFF) - getByte( inpFp ); - } - - // don't lower this value, backreferences can be 64kb far away -#define HISTORY_SIZE 64 * 1024 - // contains the latest decoded data - unsigned char history[ HISTORY_SIZE ]; - // next free position in history[] - unsigned int pos = 0; - - // dictionary compression is a recently introduced feature, just move its contents to the buffer - if ( dictionary != NULL ) { - // open dictionary - FILE *dict = fopen( dictionary, "rb" ); - - if ( !dict ) { - error( "cannot open dictionary" ); - } - - // get dictionary's filesize - fseek( dict, 0, SEEK_END ); - size_t dictSize = ftell( dict ); - // only the last 64k are relevant - size_t relevant = dictSize < 65536 ? 0 : dictSize - 65536; - fseek( dict, (long)relevant, SEEK_SET ); - - if ( dictSize > 65536 ) { - dictSize = 65536; - } - - // read it and store it at the end of the buffer - fread( history + HISTORY_SIZE - dictSize, 1, dictSize, dict ); - fclose( dict ); - } - - // parse all blocks until blockSize == 0 - while ( 1 ) { - // block size - uint32_t blockSize = getByte( inpFp ); - blockSize |= (uint32_t)getByte( inpFp ) << 8; - blockSize |= (uint32_t)getByte( inpFp ) << 16; - blockSize |= (uint32_t)getByte( inpFp ) << 24; - - // highest bit set ? - unsigned char isCompressed = isLegacy || (blockSize & 0x80000000) == 0; - - if ( isModern ) { - blockSize &= 0x7FFFFFFF; - } - - // stop after last block - if ( blockSize == 0 ) { - break; - } - - if ( isCompressed ) { - // decompress block - uint32_t blockOffset = 0; - while ( blockOffset < blockSize ) { - // get a token - unsigned char token = getByte( inpFp ); - - blockOffset++; - - // determine number of literals - uint32_t numLiterals = (token >> 4) & 0x0F; - - if ( numLiterals == 15 ) { - // number of literals length encoded in more than 1 byte - unsigned char current; - do{ - current = getByte( inpFp ); - numLiterals += current; - blockOffset++; - } while ( current == 255 ); - } - - blockOffset += numLiterals; - // copy all those literals - while ( numLiterals-- > 0 ) { - history[ pos++ ] = getByte( inpFp ); - - // flush output buffer - if ( pos == HISTORY_SIZE ) { - sendBytes( history, HISTORY_SIZE, outFp ); - pos = 0; - } - } - - // last token has only literals - if ( blockOffset == blockSize ) { - break; - } - - // match distance is encoded by two bytes (little endian) - blockOffset += 2; - uint32_t delta = getByte( inpFp ); - delta |= (uint32_t)getByte( inpFp ) << 8; - - // zero isn't allowed - if ( delta == 0 ) { - error( "invalid offset" ); - } - - // match length (must be >= 4, therefore length is stored minus 4) - uint32_t matchLength = 4 + (token & 0x0F); - - if ( matchLength == 4 + 0x0F ) { - unsigned char current; - do{ // match length encoded in more than 1 byte - current = getByte( inpFp ); - matchLength += current; - blockOffset++; - } while ( current == 255 ); - } - - // copy match - uint32_t reference = (pos >= delta) ? pos - delta : HISTORY_SIZE + pos - delta; - - if ( (pos + matchLength < HISTORY_SIZE) && (reference + matchLength < HISTORY_SIZE) ) { - // fast copy - if ( (pos >= reference + matchLength) || (reference >= pos + matchLength) ) { - // non-overlapping - memcpy( history + pos, history + reference, matchLength ); - pos += matchLength; - } - else { - // overlapping - while ( matchLength-- > 0 ) { - history[ pos++ ] = history[ reference++ ]; - } - } - } - else { - // slower copy, have to take care of buffer limits - while ( matchLength-- > 0 ) { - // copy single byte - history[ pos++ ] = history[ reference++ ]; - - // cannot write anymore ? => wrap around - if ( pos == HISTORY_SIZE ) { - // flush output buffer - sendBytes( history, HISTORY_SIZE, outFp ); - pos = 0; - } - - // cannot read anymore ? => wrap around - if ( reference == HISTORY_SIZE ) { - reference = 0; - } - } - } - } - - // all legacy blocks must be completely filled - except for the last one - if ( isLegacy && (blockSize < 8 * 1024 * 1024) ) { - break; - } - } - else { - // copy uncompressed data and add to history, too (if next block is compressed and some matches - // refer to this block) - while ( blockSize-- > 0 ) { - // copy a byte ... - history[ pos++ ] = getByte( inpFp ); - - // ... until buffer is full => send to output - if ( pos == HISTORY_SIZE ) { - sendBytes( history, HISTORY_SIZE, outFp ); - pos = 0; - } - } - } - - if ( hasBlockChecksum ) { - // ignore checksum, skip 4 bytes - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - } - } - - if ( hasContentChecksum ) { - // ignore checksum, skip 4 bytes - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - getByte( inpFp ); - } - - // flush output buffer - sendBytes( history, pos, outFp ); +void unlz4( const char *ifn, const char *ofn, const char* dictionary) { + + FILE *inpFp = fopen( ifn, "rb" ); + FILE *outFp = fopen( ofn, "wb" ); + + // signature + unsigned char signature1 = getByte( inpFp ); + unsigned char signature2 = getByte( inpFp ); + unsigned char signature3 = getByte( inpFp ); + unsigned char signature4 = getByte( inpFp ); + uint32_t signature = (signature4 << 24) | (signature3 << 16) | (signature2 << 8) | signature1; + int isModern = (signature == 0x184D2204); + int isLegacy = (signature == 0x184C2102); + if (!isModern && !isLegacy) + error("invalid signature"); + + unsigned char hasBlockChecksum = 0; + unsigned char hasContentSize = 0; + unsigned char hasContentChecksum = 0; + unsigned char hasDictionaryID = 0; + if (isModern) { + // flags + unsigned char flags = getByte( inpFp ); + hasBlockChecksum = flags & 16; + hasContentSize = flags & 8; + hasContentChecksum = flags & 4; + hasDictionaryID = flags & 1; + + // only version 1 file format + unsigned char version = flags >> 6; + if (version != 1) + error("only LZ4 file format version 1 supported"); + + // ignore blocksize + getByte( inpFp ); + + if (hasContentSize) + { + // ignore, skip 8 bytes + getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); + getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); + } + if (hasDictionaryID) + { + // ignore, skip 4 bytes + getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); + } + + // ignore header checksum (xxhash32 of everything up this point & 0xFF) + getByte( inpFp ); + } + + // don't lower this value, backreferences can be 64kb far away +#define HISTORY_SIZE 64*1024 + // contains the latest decoded data + unsigned char history[HISTORY_SIZE]; + // next free position in history[] + unsigned int pos = 0; + + // dictionary compression is a recently introduced feature, just move its contents to the buffer + if (dictionary != NULL) + { + // open dictionary + FILE* dict = fopen(dictionary, "rb"); + if (!dict) + error("cannot open dictionary"); + + // get dictionary's filesize + fseek(dict, 0, SEEK_END); + size_t dictSize = ftell(dict); + // only the last 64k are relevant + size_t relevant = dictSize < 65536 ? 0 : dictSize - 65536; + fseek(dict, (long)relevant, SEEK_SET); + if (dictSize > 65536) + dictSize = 65536; + // read it and store it at the end of the buffer + fread(history + HISTORY_SIZE - dictSize, 1, dictSize, dict); + fclose(dict); + } + + // parse all blocks until blockSize == 0 + while (1) + { + // block size + uint32_t blockSize = getByte( inpFp ); + blockSize |= (uint32_t)getByte( inpFp ) << 8; + blockSize |= (uint32_t)getByte( inpFp ) << 16; + blockSize |= (uint32_t)getByte( inpFp ) << 24; + + // highest bit set ? + unsigned char isCompressed = isLegacy || (blockSize & 0x80000000) == 0; + if (isModern) + blockSize &= 0x7FFFFFFF; + + // stop after last block + if (blockSize == 0) + break; + + if (isCompressed) + { + // decompress block + uint32_t blockOffset = 0; + while (blockOffset < blockSize) + { + // get a token + unsigned char token = getByte( inpFp ); + + blockOffset++; + + // determine number of literals + uint32_t numLiterals = (token >> 4) & 0x0F; + if (numLiterals == 15) + { + // number of literals length encoded in more than 1 byte + unsigned char current; + do + { + current = getByte( inpFp ); + numLiterals += current; + blockOffset++; + } while (current == 255); + } + + blockOffset += numLiterals; + // copy all those literals + while (numLiterals-- > 0) + { + history[pos++] = getByte( inpFp ); + + // flush output buffer + if (pos == HISTORY_SIZE) + { + sendBytes(history, HISTORY_SIZE, outFp); + pos = 0; + } + } + + // last token has only literals + if (blockOffset == blockSize) + break; + + // match distance is encoded by two bytes (little endian) + blockOffset += 2; + uint32_t delta = getByte( inpFp ); + delta |= (uint32_t)getByte( inpFp ) << 8; + // zero isn't allowed + if (delta == 0) + error("invalid offset"); + + // match length (must be >= 4, therefore length is stored minus 4) + uint32_t matchLength = 4 + (token & 0x0F); + if (matchLength == 4 + 0x0F) + { + unsigned char current; + do // match length encoded in more than 1 byte + { + current = getByte( inpFp ); + matchLength += current; + blockOffset++; + } while (current == 255); + } + + // copy match + uint32_t reference = (pos >= delta) ? pos - delta : HISTORY_SIZE + pos - delta; + if (pos + matchLength < HISTORY_SIZE && reference + matchLength < HISTORY_SIZE) + { + // fast copy + if (pos >= reference + matchLength || reference >= pos + matchLength) + { + // non-overlapping + memcpy(history + pos, history + reference, matchLength); + pos += matchLength; + } + else + { + // overlapping + while (matchLength-- > 0) + history[pos++] = history[reference++]; + } + } + else + { + // slower copy, have to take care of buffer limits + while (matchLength-- > 0) + { + // copy single byte + history[pos++] = history[reference++]; + + // cannot write anymore ? => wrap around + if (pos == HISTORY_SIZE) + { + // flush output buffer + sendBytes(history, HISTORY_SIZE, outFp); + pos = 0; + } + // cannot read anymore ? => wrap around + if (reference == HISTORY_SIZE) + reference = 0; + } + } + } + + // all legacy blocks must be completely filled - except for the last one + if (isLegacy && blockSize < 8*1024*1024) + break; + } + else + { + // copy uncompressed data and add to history, too (if next block is compressed and some matches refer to this block) + while (blockSize-- > 0) + { + // copy a byte ... + history[pos++] = getByte( inpFp ); + // ... until buffer is full => send to output + if (pos == HISTORY_SIZE) + { + sendBytes(history, HISTORY_SIZE, outFp); + pos = 0; + } + } + } + + if (hasBlockChecksum) + { + // ignore checksum, skip 4 bytes + getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); + } + } + + if (hasContentChecksum) + { + // ignore checksum, skip 4 bytes + getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); getByte( inpFp ); + } + + // flush output buffer + sendBytes(history, pos, outFp); } diff --git lib/lz4dec.h lib/lz4dec.h index daf72d2..e1d1a96 100644 --- lib/lz4dec.h +++ lib/lz4dec.h @@ -1,7 +1,9 @@ -/** - * lz4dec.h - Lz4 decompression header - **/ +/* + * + * lz4dec.h - Lz4 decompression header + * + */ #pragma once -void unlz4( const char *, const char *, const char * ); +void unlz4(const char *, const char *, const char *); diff --git lib/meson.build lib/meson.build index ff87c69..a95b987 100644 --- lib/meson.build +++ lib/meson.build @@ -16,6 +16,36 @@ Sources = [ 'lz4dec.c' ] +if ( QtCore.version().startswith( '4.' ) ) + warning( 'We strongly advice against using Qt4.' ) + + Headers += [ + 'MimeHandler/NBMimeDatabase.hpp', + 'MimeHandler/NBMimeDatabase_p.hpp', + 'MimeHandler/NBMimeGlobPattern_p.hpp', + 'MimeHandler/NBMimeMagicRule_p.hpp', + 'MimeHandler/NBMimeMagicRuleMatcher_p.hpp', + 'MimeHandler/NBMimeProvider_p.hpp', + 'MimeHandler/NBMimeType.hpp', + 'MimeHandler/NBMimeType_p.hpp', + 'MimeHandler/NBMimeTypeParser_p.hpp', + 'StandardPaths/NBStandardPaths.hpp' + ] + + Sources += [ + 'MimeHandler/NBMimeDatabase.cpp', + 'MimeHandler/NBMimeDatabase.cpp', + 'MimeHandler/NBMimeGlobPattern.cpp', + 'MimeHandler/NBMimeMagicRule.cpp', + 'MimeHandler/NBMimeMagicRuleMatcher.cpp', + 'MimeHandler/NBMimeProvider.cpp', + 'MimeHandler/NBMimeType.cpp', + 'MimeHandler/NBMimeTypeParser.cpp', + 'StandardPaths/NBStandardPaths_unix.cpp', + 'StandardPaths/NBStandardPaths.cpp' + ] +endif + Mocs = Qt.compile_moc( headers : Headers, dependencies: Deps @@ -23,8 +53,8 @@ Mocs = Qt.compile_moc( shared = shared_library( TgtName, [ Sources, Mocs ], - version: meson.project_version(), - include_directories: Includes, + version: meson.project_version(), + include_directories: Includes, dependencies: Deps, install: true, link_args: ['-rdynamic','-fPIC'], @@ -33,7 +63,7 @@ shared = shared_library( static = static_library( TgtName, [ Sources, Mocs ], - include_directories: Includes, + include_directories: Includes, dependencies: Deps, install: get_option( 'install_static' ), install_dir: join_paths( get_option( 'libdir' ) ) @@ -44,21 +74,21 @@ install_headers( ) ArchiveQtShared = declare_dependency( - link_with: shared, + link_with: shared, ) ArchiveQtStatic = declare_dependency( - link_with: static, + link_with: static, ) ## PkgConfig Section pkgconfig = import( 'pkgconfig' ) pkgconfig.generate( - shared, - name: TgtName, - version: meson.project_version(), - filebase: TgtName, - description: 'A Qt based archiving solution with libarchive backend', - requires: [ QtCore.name(), Zlib.name(), Lzma.name(), Archive.name() ], + shared, + name: TgtName, + version: meson.project_version(), + filebase: TgtName, + description: 'A Qt based archiving solution with libarchive backend', + requires: [ QtCore.name(), Zlib.name(), Lzma.name(), Archive.name() ], libraries: [ '-lbz2' ] ) diff --git meson.build meson.build index 1cd1cca..7ad65d8 100644 --- meson.build +++ meson.build @@ -2,7 +2,7 @@ project( 'Archive Qt', 'c', 'cpp', - version: '2.0.8', + version: '2.0.7', license: 'GPLv3', meson_version: '>=0.59.0', default_options: [ @@ -21,24 +21,43 @@ Lzma = dependency( 'liblzma' ) BZip = meson.get_compiler( 'cpp' ).find_library( 'bz2' ) Archive = dependency( 'libarchive' ) -if get_option('use_qt_version') == 'qt5' - Qt = import( 'qt5' ) - QtCore = dependency( 'Qt5Core' ) - TgtName = 'archiveqt5' +if get_option('use_qt_version') == 'qt4' + Qt = import( 'qt4' ) + QtCore = dependency( 'QtCore' ) + TgtName = 'archiveqt4' + +elif get_option('use_qt_version') == 'qt5' + Qt = import( 'qt5' ) + QtCore = dependency( 'Qt5Core' ) + TgtName = 'archiveqt5' elif get_option('use_qt_version') == 'qt6' - Qt = import( 'qt6' ) - QtCore = dependency( 'Qt6Core' ) - TgtName = 'archiveqt6' + Qt = import( 'qt6' ) + QtCore = dependency( 'Qt6Core' ) + TgtName = 'archiveqt6' endif Deps = [ QtCore, Zlib, Lzma, BZip, Archive ] -Includes = include_directories( 'lib' ) +Includes = include_directories( + 'lib', + 'lib/MimeHandler', + 'lib/StandardPaths', +) # Contains the main library subdir( 'lib' ) # Contains the archvier cli app subdir( 'archiver' ) + + +summary = [ + '', + '----------------', + 'Archive Qt @0@'.format(meson.project_version()), + '----------------', + '' +] +message('\n'.join(summary)) diff --git meson_options.txt meson_options.txt index 157d261..62c66b9 100644 --- meson_options.txt +++ meson_options.txt @@ -1,7 +1,7 @@ option( 'use_qt_version', type: 'combo', - choices: ['qt5', 'qt6'], + choices: ['qt4', 'qt5', 'qt6'], value: 'qt5', description: 'Select the Qt version to use' )