]> git.lyx.org Git - lyx.git/blob - src/support/minizip/zipunzip.cpp
Replace the text class shared ptr by good old index-into-global-list.
[lyx.git] / src / support / minizip / zipunzip.cpp
1 /* zipunzip.cpp -- IO for compress .zip files using zlib
2    Version 1.01e, February 12th, 2005
3
4    Copyright (C) 1998-2005 Gilles Vollant
5
6    This unzip package allow creates .ZIP file, compatible with PKZip 2.04g
7      WinZip, InfoZip tools and compatible.
8    Multi volume ZipFile (span) are not supported.
9    Encryption compatible with pkzip 2.04g only supported
10    Old compressions used by old PKZip 1.x are not supported
11
12   For uncompress .zip file, look at unzip.h
13
14
15    I WAIT FEEDBACK at mail info@winimage.com
16    Visit also http://www.winimage.com/zLibDll/unzip.html for evolution
17
18    Condition of use and distribution are the same than zlib :
19
20   This software is provided 'as-is', without any express or implied
21   warranty.  In no event will the authors be held liable for any damages
22   arising from the use of this software.
23
24   Permission is granted to anyone to use this software for any purpose,
25   including commercial applications, and to alter it and redistribute it
26   freely, subject to the following restrictions:
27
28   1. The origin of this software must not be misrepresented; you must not
29      claim that you wrote the original software. If you use this software
30      in a product, an acknowledgment in the product documentation would be
31      appreciated but is not required.
32   2. Altered source versions must be plainly marked as such, and must not be
33      misrepresented as being the original software.
34   3. This notice may not be removed or altered from any source distribution.
35
36
37 */
38
39 /* for more info about .ZIP format, see
40       http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
41       http://www.info-zip.org/pub/infozip/doc/
42    PkWare has also a specification at :
43       ftp://ftp.pkware.com/probdesc.zip
44 */
45
46
47 // The original minizip.c and miniunz.c distributed with minizip provide
48 // two command line tools minizip and miniunz. This file combines these two 
49 // files and modifies the interface to provide two functions zipFiles and 
50 // unzipToDir. This file is covered by the original minizip license.
51 // 
52
53 #include <config.h>
54
55 #include <cstdlib>
56 #include <cstring>
57 #include <string>
58 #include <vector>
59 #include <utility>
60 #include "support/FileName.h"
61
62 #ifdef HAVE_UNISTD_H
63 # include <unistd.h>
64 #endif
65 #ifdef HAVE_DIRECT_H
66 # include <direct.h>
67 #endif
68 #ifdef HAVE_SYS_TYPES_H
69 # include <sys/types.h>
70 #endif
71 #ifdef HAVE_SYS_STAT_H
72 # include <sys/stat.h>
73 #endif
74 #ifdef HAVE_IO_H
75 # include <io.h>
76 #endif
77 #ifdef HAVE_SYS_UTIME_H
78 # include <sys/utime.h>
79 #endif
80 #ifdef HAVE_UTIME_H
81 # include <utime.h>
82 #endif
83 #include <fcntl.h>
84
85 #include "zip.h"
86 #include "unzip.h"
87
88 #ifdef WIN32
89 #define USEWIN32IOAPI
90 #include "iowin32.h"
91 #endif
92
93 #define WRITEBUFFERSIZE (16384)
94 #define MAXFILENAME (256)
95
96 using namespace std;
97 using lyx::support::FileName;
98
99
100 #ifdef WIN32
101 uLong filetime(const char * f, tm_zip * tmzip, uLong * dt)
102 {
103         int ret = 0;
104         {
105                 FILETIME ftLocal;
106                 HANDLE hFind;
107                 WIN32_FIND_DATA  ff32;
108
109                 hFind = FindFirstFile(f,&ff32);
110                 if (hFind != INVALID_HANDLE_VALUE)
111                 {
112                         FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
113                         FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
114                         FindClose(hFind);
115                         ret = 1;
116                 }
117         }
118         return ret;
119 }
120
121 #else
122 #ifdef unix
123 uLong filetime(const char * f, tm_zip * tmzip, uLong * /*dt*/)
124 {
125         int ret=0;
126         struct stat s;                                                            /* results of stat() */
127         struct tm* filedate;
128         time_t tm_t=0;
129
130         if (strcmp(f,"-")!=0) {
131                 char name[MAXFILENAME+1];
132                 int len = strlen(f);
133                 if (len > MAXFILENAME)
134                         len = MAXFILENAME;
135
136                 strncpy(name, f,MAXFILENAME-1);
137                 /* strncpy doesnt append the trailing NULL, of the string is too long. */
138                 name[ MAXFILENAME ] = '\0';
139
140                 if (name[len - 1] == '/')
141                         name[len - 1] = '\0';
142                 /* not all systems allow stat'ing a file with / appended */
143                 if (stat(name,&s)==0) {
144                         tm_t = s.st_mtime;
145                         ret = 1;
146                 }
147         }
148         filedate = localtime(&tm_t);
149
150         tmzip->tm_sec  = filedate->tm_sec;
151         tmzip->tm_min  = filedate->tm_min;
152         tmzip->tm_hour = filedate->tm_hour;
153         tmzip->tm_mday = filedate->tm_mday;
154         tmzip->tm_mon  = filedate->tm_mon ;
155         tmzip->tm_year = filedate->tm_year;
156
157         return ret;
158 }
159
160 #else
161
162 uLong filetime(const char * f, tm_zip * tmzip, uLong * dt)
163 {
164         return 0;
165 }
166 #endif
167 #endif
168
169 bool zipFiles(string const & zipfile, vector<pair<string, string> > const & files)
170 {
171         int err = 0;
172         zipFile zf;
173         int errclose;
174         void * buf = NULL;
175
176         int size_buf = WRITEBUFFERSIZE;
177         buf = (void*)malloc(size_buf);
178         if (buf==NULL) {
179                 printf("Error allocating memory\n");
180                 return false;
181         }
182         const char * fname = zipfile.c_str();
183
184 #ifdef USEWIN32IOAPI
185         zlib_filefunc_def ffunc;
186         fill_win32_filefunc(&ffunc);
187         // false: not append
188         zf = zipOpen2(fname, false, NULL, &ffunc);
189 #else
190         zf = zipOpen(fname, false);
191 #endif
192
193         if (zf == NULL) {
194                 return false;
195         }
196
197         for (vector<pair<string, string> >::const_iterator it = files.begin(); it != files.end(); ++it) {
198                 FILE * fin;
199                 int size_read;
200                 zip_fileinfo zi;
201                 const char * diskfilename = it->first.c_str();
202                 const char * filenameinzip = it->second.c_str();
203                 unsigned long crcFile=0;
204
205                 zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
206                         zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
207                 zi.dosDate = 0;
208                 zi.internal_fa = 0;
209                 zi.external_fa = 0;
210                 filetime(filenameinzip, &zi.tmz_date, &zi.dosDate);
211
212                 err = zipOpenNewFileInZip3(zf, filenameinzip, &zi,
213                         NULL,0,NULL,0,NULL /* comment*/,
214                         Z_DEFLATED,
215                         Z_DEFAULT_COMPRESSION,            // compression level
216                         0,
217                 /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
218                         -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
219                         NULL,                                             // password,
220                         crcFile);
221
222                 if (err != ZIP_OK) {
223                         printf("error in opening %s in zipfile\n",filenameinzip);
224                         return false;
225                 }
226                 fin = fopen(diskfilename, "rb");
227                 if (fin==NULL) {
228                         return false;
229                 }
230
231                 do {
232                         err = ZIP_OK;
233                         size_read = (int)fread(buf, 1, size_buf, fin);
234                         if (size_read < size_buf)
235                                 if (feof(fin)==0) {
236                                         printf("error in reading %s\n",filenameinzip);
237                                         return false;
238                                 }
239
240                         if (size_read>0) {
241                                 err = zipWriteInFileInZip (zf, buf, size_read);
242                                 if (err<0) {
243                                         printf("error in writing %s in the zipfile\n",
244                                                  filenameinzip);
245                                         return false;
246                                 }
247                         }
248                 } while ((err == ZIP_OK) && (size_read>0));
249
250                 if (fin)
251                         fclose(fin);
252
253                 err = zipCloseFileInZip(zf);
254                 if (err != ZIP_OK) {
255                         printf("error in closing %s in the zipfile\n",
256                                     filenameinzip);
257                         return false;
258                 }
259         }
260         errclose = zipClose(zf, NULL);
261         if (errclose != ZIP_OK) {
262                 printf("error in closing zip file\n");
263                 return false;
264         }
265         free(buf);
266         return true;
267 }
268
269 // adapted from miniunz.c
270
271 /* change_file_date : change the date/time of a file
272         filename : the filename of the file where date/time must be modified
273         dosdate : the new date at the MSDos format (4 bytes)
274         tmu_date : the SAME new date at the tm_unz format */
275 void change_file_date(const char * filename, uLong dosdate, tm_unz tmu_date)
276 {
277 #ifdef WIN32
278         HANDLE hFile;
279         FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
280
281         hFile = CreateFile(filename,GENERIC_READ | GENERIC_WRITE,
282                 0,NULL,OPEN_EXISTING,0,NULL);
283         GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
284         DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
285         LocalFileTimeToFileTime(&ftLocal,&ftm);
286         SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
287         CloseHandle(hFile);
288 #else
289 #ifdef unix
290         utimbuf ut;
291         tm newdate;
292
293         newdate.tm_sec = tmu_date.tm_sec;
294         newdate.tm_min=tmu_date.tm_min;
295         newdate.tm_hour=tmu_date.tm_hour;
296         newdate.tm_mday=tmu_date.tm_mday;
297         newdate.tm_mon=tmu_date.tm_mon;
298         if (tmu_date.tm_year > 1900)
299                 newdate.tm_year=tmu_date.tm_year - 1900;
300         else
301                 newdate.tm_year=tmu_date.tm_year ;
302         newdate.tm_isdst=-1;
303
304         ut.actime=ut.modtime=mktime(&newdate);
305         utime(filename,&ut);
306 #endif
307 #endif
308 }
309
310
311 int do_extract_currentfile(unzFile uf,
312         const int * popt_extract_without_path,
313         int * popt_overwrite,
314         const char * password,
315         const char * dirname)
316 {
317         char full_path[1024];
318         char* filename_inzip = full_path;
319         char* filename_withoutpath;
320         char* p;
321         int err=UNZ_OK;
322         FILE *fout=NULL;
323         void* buf;
324         uInt size_buf;
325
326         strcpy(full_path, dirname);
327         while ((*filename_inzip) != '\0') filename_inzip++;
328         if (*(filename_inzip-1) != '/' && *(filename_inzip-1) != '\\') {
329                 *filename_inzip = '/';
330                 *(++filename_inzip) = '\0';
331         }
332
333         unz_file_info file_info;
334         //uLong ratio=0;
335         err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,256,NULL,0,NULL,0);
336
337         if (err!=UNZ_OK) {
338                 printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
339                 return err;
340         }
341
342         size_buf = WRITEBUFFERSIZE;
343         buf = (void*)malloc(size_buf);
344         if (buf==NULL) {
345                 printf("Error allocating memory\n");
346                 return UNZ_INTERNALERROR;
347         }
348
349         p = filename_withoutpath = filename_inzip;
350         while ((*p) != '\0') {
351                 if (((*p)=='/') || ((*p)=='\\'))
352                         filename_withoutpath = p+1;
353                 p++;
354         }
355         // this is a directory
356         if ((*filename_withoutpath)=='\0') {
357                 if ((*popt_extract_without_path)==0) {
358                         printf("Create directory %s\n", filename_inzip);
359                         FileName(filename_inzip).createPath();
360                 }
361         }
362         // this is a filename
363         else {
364                 err = unzOpenCurrentFilePassword(uf,password);
365                 if (err!=UNZ_OK) {
366                         printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
367                 } else {
368                         fout=fopen(full_path, "wb");
369
370                         /* some zipfile don't contain directory alone before file */
371                         if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
372                                 (filename_withoutpath!=(char*)filename_inzip)) {
373                                 char c=*(filename_withoutpath-1);
374                                 *(filename_withoutpath-1)='\0';
375                                 printf("Create directory %s\n", full_path);
376                                 FileName(full_path).createPath();
377                                 *(filename_withoutpath-1)=c;
378                                 fout=fopen(full_path,"wb");
379                         }
380
381                         if (fout==NULL) {
382                                 printf("error opening %s\n", full_path);
383                         }
384                 }
385
386                 if (fout!=NULL) {
387
388                         do {
389                                 err = unzReadCurrentFile(uf,buf,size_buf);
390                                 if (err<0) {
391                                         printf("error %d with zipfile in unzReadCurrentFile\n",err);
392                                         break;
393                                 }
394                                 if (err>0)
395                                         if (fwrite(buf,err,1,fout)!=1) {
396                                                 printf("error in writing extracted file\n");
397                                                 err=UNZ_ERRNO;
398                                                 break;
399                                         }
400                         } while (err>0);
401                         if (fout)
402                                 fclose(fout);
403
404                         if (err==0)
405                                 change_file_date(full_path, file_info.dosDate,
406                                         file_info.tmu_date);
407                 }
408
409                 if (err==UNZ_OK) {
410                         err = unzCloseCurrentFile (uf);
411                         if (err!=UNZ_OK) {
412                                 printf("error %d with zipfile in unzCloseCurrentFile\n",err);
413                         }
414                 }
415                 else
416                         unzCloseCurrentFile(uf);          /* don't lose the error */
417         }
418
419         free(buf);
420         return err;
421 }
422
423
424 bool unzipToDir(string const & zipfile, string const & dirname)
425 {
426         unzFile uf=NULL;
427 #ifdef USEWIN32IOAPI
428         zlib_filefunc_def ffunc;
429 #endif
430
431         const char * zipfilename = zipfile.c_str();
432
433 #ifdef USEWIN32IOAPI
434         fill_win32_filefunc(&ffunc);
435         uf = unzOpen2(zipfilename, &ffunc);
436 #else
437         uf = unzOpen(zipfilename);
438 #endif
439
440         if (uf==NULL) {
441                 return false;
442         }
443
444         uLong i;
445         unz_global_info gi;
446         int err;
447         //FILE* fout=NULL;
448         int opt_extract_without_path = 0;
449         int opt_overwrite = 1;
450         char * password = NULL;
451
452         err = unzGetGlobalInfo (uf, &gi);
453         if (err != UNZ_OK) {
454                 printf("error %d with zipfile in unzGetGlobalInfo \n",err);
455                 return false;
456         }
457
458         for (i=0; i < gi.number_entry; i++) {
459                 if (do_extract_currentfile(uf, &opt_extract_without_path,
460                         &opt_overwrite,
461                         password, dirname.c_str()) != UNZ_OK)
462                         break;
463
464                 if ((i+1)<gi.number_entry) {
465                         err = unzGoToNextFile(uf);
466                         if (err != UNZ_OK) {
467                                 printf("error %d with zipfile in unzGoToNextFile\n",err);
468                                 break;
469                         }
470                 }
471         }
472
473         unzCloseCurrentFile(uf);
474     return true;
475 }
476