misc/quazip/quazip.cpp
changeset 5752 ea95ee97c805
child 7889 57b117d441b9
equal deleted inserted replaced
5750:6bbf7aee2cdf 5752:ea95ee97c805
       
     1 /*
       
     2 Copyright (C) 2005-2011 Sergey A. Tachenov
       
     3 
       
     4 This program is free software; you can redistribute it and/or modify it
       
     5 under the terms of the GNU Lesser General Public License as published by
       
     6 the Free Software Foundation; either version 2 of the License, or (at
       
     7 your option) any later version.
       
     8 
       
     9 This program is distributed in the hope that it will be useful, but
       
    10 WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
       
    12 General Public License for more details.
       
    13 
       
    14 You should have received a copy of the GNU Lesser General Public License
       
    15 along with this program; if not, write to the Free Software Foundation,
       
    16 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
       
    17 
       
    18 See COPYING file for the full LGPL text.
       
    19 
       
    20 Original ZIP package is copyrighted by Gilles Vollant, see
       
    21 quazip/(un)zip.h files for details, basically it's zlib license.
       
    22  **/
       
    23 
       
    24 #include <QFile>
       
    25 
       
    26 #include "quazip.h"
       
    27 
       
    28 class QuaZipPrivate {
       
    29   friend class QuaZip;
       
    30   private:
       
    31     QTextCodec *fileNameCodec, *commentCodec;
       
    32     QString zipName;
       
    33     QIODevice *ioDevice;
       
    34     QString comment;
       
    35     QuaZip::Mode mode;
       
    36     union {
       
    37       unzFile unzFile_f;
       
    38       zipFile zipFile_f;
       
    39     };
       
    40     bool hasCurrentFile_f;
       
    41     int zipError;
       
    42     inline QuaZipPrivate():
       
    43       fileNameCodec(QTextCodec::codecForLocale()),
       
    44       commentCodec(QTextCodec::codecForLocale()),
       
    45       ioDevice(NULL),
       
    46       mode(QuaZip::mdNotOpen),
       
    47       hasCurrentFile_f(false),
       
    48       zipError(UNZ_OK) {}
       
    49     inline QuaZipPrivate(const QString &zipName):
       
    50       fileNameCodec(QTextCodec::codecForLocale()),
       
    51       commentCodec(QTextCodec::codecForLocale()),
       
    52       zipName(zipName),
       
    53       ioDevice(NULL),
       
    54       mode(QuaZip::mdNotOpen),
       
    55       hasCurrentFile_f(false),
       
    56       zipError(UNZ_OK) {}
       
    57     inline QuaZipPrivate(QIODevice *ioDevice):
       
    58       fileNameCodec(QTextCodec::codecForLocale()),
       
    59       commentCodec(QTextCodec::codecForLocale()),
       
    60       ioDevice(ioDevice),
       
    61       mode(QuaZip::mdNotOpen),
       
    62       hasCurrentFile_f(false),
       
    63       zipError(UNZ_OK) {}
       
    64 };
       
    65 
       
    66 QuaZip::QuaZip():
       
    67   p(new QuaZipPrivate())
       
    68 {
       
    69 }
       
    70 
       
    71 QuaZip::QuaZip(const QString& zipName):
       
    72   p(new QuaZipPrivate(zipName))
       
    73 {
       
    74 }
       
    75 
       
    76 QuaZip::QuaZip(QIODevice *ioDevice):
       
    77   p(new QuaZipPrivate(ioDevice))
       
    78 {
       
    79 }
       
    80 
       
    81 QuaZip::~QuaZip()
       
    82 {
       
    83   if(isOpen())
       
    84     close();
       
    85   delete p;
       
    86 }
       
    87 
       
    88 bool QuaZip::open(Mode mode, zlib_filefunc_def* ioApi)
       
    89 {
       
    90   p->zipError=UNZ_OK;
       
    91   if(isOpen()) {
       
    92     qWarning("QuaZip::open(): ZIP already opened");
       
    93     return false;
       
    94   }
       
    95   QIODevice *ioDevice = p->ioDevice;
       
    96   if (ioDevice == NULL) {
       
    97     if (p->zipName.isEmpty()) {
       
    98       qWarning("QuaZip::open(): set either ZIP file name or IO device first");
       
    99       return false;
       
   100     } else {
       
   101       ioDevice = new QFile(p->zipName);
       
   102     }
       
   103   }
       
   104   switch(mode) {
       
   105     case mdUnzip:
       
   106       p->unzFile_f=unzOpen2(ioDevice, ioApi);
       
   107       if(p->unzFile_f!=NULL) {
       
   108         p->mode=mode;
       
   109         p->ioDevice = ioDevice;
       
   110         return true;
       
   111       } else {
       
   112         p->zipError=UNZ_OPENERROR;
       
   113         if (!p->zipName.isEmpty())
       
   114           delete ioDevice;
       
   115         return false;
       
   116       }
       
   117     case mdCreate:
       
   118     case mdAppend:
       
   119     case mdAdd:
       
   120       p->zipFile_f=zipOpen2(ioDevice,
       
   121           mode==mdCreate?APPEND_STATUS_CREATE:
       
   122           mode==mdAppend?APPEND_STATUS_CREATEAFTER:
       
   123           APPEND_STATUS_ADDINZIP,
       
   124           NULL,
       
   125           ioApi);
       
   126       if(p->zipFile_f!=NULL) {
       
   127         p->mode=mode;
       
   128         p->ioDevice = ioDevice;
       
   129         return true;
       
   130       } else {
       
   131         p->zipError=UNZ_OPENERROR;
       
   132         if (!p->zipName.isEmpty())
       
   133           delete ioDevice;
       
   134         return false;
       
   135       }
       
   136     default:
       
   137       qWarning("QuaZip::open(): unknown mode: %d", (int)mode);
       
   138       if (!p->zipName.isEmpty())
       
   139         delete ioDevice;
       
   140       return false;
       
   141       break;
       
   142   }
       
   143 }
       
   144 
       
   145 void QuaZip::close()
       
   146 {
       
   147   p->zipError=UNZ_OK;
       
   148   switch(p->mode) {
       
   149     case mdNotOpen:
       
   150       qWarning("QuaZip::close(): ZIP is not open");
       
   151       return;
       
   152     case mdUnzip:
       
   153       p->zipError=unzClose(p->unzFile_f);
       
   154       break;
       
   155     case mdCreate:
       
   156     case mdAppend:
       
   157     case mdAdd:
       
   158       p->zipError=zipClose(p->zipFile_f, p->commentCodec->fromUnicode(p->comment).constData());
       
   159       break;
       
   160     default:
       
   161       qWarning("QuaZip::close(): unknown mode: %d", (int)p->mode);
       
   162       return;
       
   163   }
       
   164   // opened by name, need to delete the internal IO device
       
   165   if (!p->zipName.isEmpty())
       
   166     delete p->ioDevice;
       
   167   if(p->zipError==UNZ_OK)
       
   168     p->mode=mdNotOpen;
       
   169 }
       
   170 
       
   171 void QuaZip::setZipName(const QString& zipName)
       
   172 {
       
   173   if(isOpen()) {
       
   174     qWarning("QuaZip::setZipName(): ZIP is already open!");
       
   175     return;
       
   176   }
       
   177   p->zipName=zipName;
       
   178   p->ioDevice = NULL;
       
   179 }
       
   180 
       
   181 void QuaZip::setIoDevice(QIODevice *ioDevice)
       
   182 {
       
   183   if(isOpen()) {
       
   184     qWarning("QuaZip::setIoDevice(): ZIP is already open!");
       
   185     return;
       
   186   }
       
   187   p->ioDevice = ioDevice;
       
   188   p->zipName = QString();
       
   189 }
       
   190 
       
   191 int QuaZip::getEntriesCount()const
       
   192 {
       
   193   QuaZip *fakeThis=(QuaZip*)this; // non-const
       
   194   fakeThis->p->zipError=UNZ_OK;
       
   195   if(p->mode!=mdUnzip) {
       
   196     qWarning("QuaZip::getEntriesCount(): ZIP is not open in mdUnzip mode");
       
   197     return -1;
       
   198   }
       
   199   unz_global_info globalInfo;
       
   200   if((fakeThis->p->zipError=unzGetGlobalInfo(p->unzFile_f, &globalInfo))!=UNZ_OK)
       
   201     return p->zipError;
       
   202   return (int)globalInfo.number_entry;
       
   203 }
       
   204 
       
   205 QString QuaZip::getComment()const
       
   206 {
       
   207   QuaZip *fakeThis=(QuaZip*)this; // non-const
       
   208   fakeThis->p->zipError=UNZ_OK;
       
   209   if(p->mode!=mdUnzip) {
       
   210     qWarning("QuaZip::getComment(): ZIP is not open in mdUnzip mode");
       
   211     return QString();
       
   212   }
       
   213   unz_global_info globalInfo;
       
   214   QByteArray comment;
       
   215   if((fakeThis->p->zipError=unzGetGlobalInfo(p->unzFile_f, &globalInfo))!=UNZ_OK)
       
   216     return QString();
       
   217   comment.resize(globalInfo.size_comment);
       
   218   if((fakeThis->p->zipError=unzGetGlobalComment(p->unzFile_f, comment.data(), comment.size())) < 0)
       
   219     return QString();
       
   220   fakeThis->p->zipError = UNZ_OK;
       
   221   return p->commentCodec->toUnicode(comment);
       
   222 }
       
   223 
       
   224 bool QuaZip::setCurrentFile(const QString& fileName, CaseSensitivity cs)
       
   225 {
       
   226   p->zipError=UNZ_OK;
       
   227   if(p->mode!=mdUnzip) {
       
   228     qWarning("QuaZip::setCurrentFile(): ZIP is not open in mdUnzip mode");
       
   229     return false;
       
   230   }
       
   231   if(fileName.isEmpty()) {
       
   232     p->hasCurrentFile_f=false;
       
   233     return true;
       
   234   }
       
   235   // Unicode-aware reimplementation of the unzLocateFile function
       
   236   if(p->unzFile_f==NULL) {
       
   237     p->zipError=UNZ_PARAMERROR;
       
   238     return false;
       
   239   }
       
   240   if(fileName.length()>MAX_FILE_NAME_LENGTH) {
       
   241     p->zipError=UNZ_PARAMERROR;
       
   242     return false;
       
   243   }
       
   244   bool sens;
       
   245   if(cs==csDefault) {
       
   246 #ifdef Q_WS_WIN
       
   247     sens=false;
       
   248 #else
       
   249     sens=true;
       
   250 #endif
       
   251   } else sens=cs==csSensitive;
       
   252   QString lower, current;
       
   253   if(!sens) lower=fileName.toLower();
       
   254   p->hasCurrentFile_f=false;
       
   255   for(bool more=goToFirstFile(); more; more=goToNextFile()) {
       
   256     current=getCurrentFileName();
       
   257     if(current.isEmpty()) return false;
       
   258     if(sens) {
       
   259       if(current==fileName) break;
       
   260     } else {
       
   261       if(current.toLower()==lower) break;
       
   262     }
       
   263   }
       
   264   return p->hasCurrentFile_f;
       
   265 }
       
   266 
       
   267 bool QuaZip::goToFirstFile()
       
   268 {
       
   269   p->zipError=UNZ_OK;
       
   270   if(p->mode!=mdUnzip) {
       
   271     qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode");
       
   272     return false;
       
   273   }
       
   274   p->zipError=unzGoToFirstFile(p->unzFile_f);
       
   275   p->hasCurrentFile_f=p->zipError==UNZ_OK;
       
   276   return p->hasCurrentFile_f;
       
   277 }
       
   278 
       
   279 bool QuaZip::goToNextFile()
       
   280 {
       
   281   p->zipError=UNZ_OK;
       
   282   if(p->mode!=mdUnzip) {
       
   283     qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode");
       
   284     return false;
       
   285   }
       
   286   p->zipError=unzGoToNextFile(p->unzFile_f);
       
   287   p->hasCurrentFile_f=p->zipError==UNZ_OK;
       
   288   if(p->zipError==UNZ_END_OF_LIST_OF_FILE)
       
   289     p->zipError=UNZ_OK;
       
   290   return p->hasCurrentFile_f;
       
   291 }
       
   292 
       
   293 bool QuaZip::getCurrentFileInfo(QuaZipFileInfo *info)const
       
   294 {
       
   295   QuaZip *fakeThis=(QuaZip*)this; // non-const
       
   296   fakeThis->p->zipError=UNZ_OK;
       
   297   if(p->mode!=mdUnzip) {
       
   298     qWarning("QuaZip::getCurrentFileInfo(): ZIP is not open in mdUnzip mode");
       
   299     return false;
       
   300   }
       
   301   unz_file_info info_z;
       
   302   QByteArray fileName;
       
   303   QByteArray extra;
       
   304   QByteArray comment;
       
   305   if(info==NULL) return false;
       
   306   if(!isOpen()||!hasCurrentFile()) return false;
       
   307   if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, &info_z, NULL, 0, NULL, 0, NULL, 0))!=UNZ_OK)
       
   308     return false;
       
   309   fileName.resize(info_z.size_filename);
       
   310   extra.resize(info_z.size_file_extra);
       
   311   comment.resize(info_z.size_file_comment);
       
   312   if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, NULL,
       
   313       fileName.data(), fileName.size(),
       
   314       extra.data(), extra.size(),
       
   315       comment.data(), comment.size()))!=UNZ_OK)
       
   316     return false;
       
   317   info->versionCreated=info_z.version;
       
   318   info->versionNeeded=info_z.version_needed;
       
   319   info->flags=info_z.flag;
       
   320   info->method=info_z.compression_method;
       
   321   info->crc=info_z.crc;
       
   322   info->compressedSize=info_z.compressed_size;
       
   323   info->uncompressedSize=info_z.uncompressed_size;
       
   324   info->diskNumberStart=info_z.disk_num_start;
       
   325   info->internalAttr=info_z.internal_fa;
       
   326   info->externalAttr=info_z.external_fa;
       
   327   info->name=p->fileNameCodec->toUnicode(fileName);
       
   328   info->comment=p->commentCodec->toUnicode(comment);
       
   329   info->extra=extra;
       
   330   info->dateTime=QDateTime(
       
   331       QDate(info_z.tmu_date.tm_year, info_z.tmu_date.tm_mon+1, info_z.tmu_date.tm_mday),
       
   332       QTime(info_z.tmu_date.tm_hour, info_z.tmu_date.tm_min, info_z.tmu_date.tm_sec));
       
   333   return true;
       
   334 }
       
   335 
       
   336 QString QuaZip::getCurrentFileName()const
       
   337 {
       
   338   QuaZip *fakeThis=(QuaZip*)this; // non-const
       
   339   fakeThis->p->zipError=UNZ_OK;
       
   340   if(p->mode!=mdUnzip) {
       
   341     qWarning("QuaZip::getCurrentFileName(): ZIP is not open in mdUnzip mode");
       
   342     return QString();
       
   343   }
       
   344   if(!isOpen()||!hasCurrentFile()) return QString();
       
   345   QByteArray fileName(MAX_FILE_NAME_LENGTH, 0);
       
   346   if((fakeThis->p->zipError=unzGetCurrentFileInfo(p->unzFile_f, NULL, fileName.data(), fileName.size(),
       
   347       NULL, 0, NULL, 0))!=UNZ_OK)
       
   348     return QString();
       
   349   return p->fileNameCodec->toUnicode(fileName.constData());
       
   350 }
       
   351 
       
   352 void QuaZip::setFileNameCodec(QTextCodec *fileNameCodec)
       
   353 {
       
   354   p->fileNameCodec=fileNameCodec;
       
   355 }
       
   356 
       
   357 void QuaZip::setFileNameCodec(const char *fileNameCodecName)
       
   358 {
       
   359   p->fileNameCodec=QTextCodec::codecForName(fileNameCodecName);
       
   360 }
       
   361 
       
   362 QTextCodec *QuaZip::getFileNameCodec()const
       
   363 {
       
   364   return p->fileNameCodec;
       
   365 }
       
   366 
       
   367 void QuaZip::setCommentCodec(QTextCodec *commentCodec)
       
   368 {
       
   369   p->commentCodec=commentCodec;
       
   370 }
       
   371 
       
   372 void QuaZip::setCommentCodec(const char *commentCodecName)
       
   373 {
       
   374   p->commentCodec=QTextCodec::codecForName(commentCodecName);
       
   375 }
       
   376 
       
   377 QTextCodec *QuaZip::getCommentCodec()const
       
   378 {
       
   379   return p->commentCodec;
       
   380 }
       
   381 
       
   382 QString QuaZip::getZipName() const
       
   383 {
       
   384   return p->zipName;
       
   385 }
       
   386 
       
   387 QIODevice *QuaZip::getIoDevice() const
       
   388 {
       
   389   if (!p->zipName.isEmpty()) // opened by name, using an internal QIODevice
       
   390     return NULL;
       
   391   return p->ioDevice;
       
   392 }
       
   393 
       
   394 QuaZip::Mode QuaZip::getMode()const
       
   395 {
       
   396   return p->mode;
       
   397 }
       
   398 
       
   399 bool QuaZip::isOpen()const
       
   400 {
       
   401   return p->mode!=mdNotOpen;
       
   402 }
       
   403 
       
   404 int QuaZip::getZipError() const
       
   405 {
       
   406   return p->zipError;
       
   407 }
       
   408 
       
   409 void QuaZip::setComment(const QString& comment)
       
   410 {
       
   411   p->comment=comment;
       
   412 }
       
   413 
       
   414 bool QuaZip::hasCurrentFile()const
       
   415 {
       
   416   return p->hasCurrentFile_f;
       
   417 }
       
   418 
       
   419 unzFile QuaZip::getUnzFile()
       
   420 {
       
   421   return p->unzFile_f;
       
   422 }
       
   423 
       
   424 zipFile QuaZip::getZipFile()
       
   425 {
       
   426   return p->zipFile_f;
       
   427 }