]> git.lyx.org Git - lyx.git/blob - src/support/minizip/zipunzip.cpp
Embedding: merge lyx::EmbeddedFiles to lyx::support::EmbeddedFileList
[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 <string>
56 #include <vector>
57 #include <utility>
58 #include "support/FileName.h"
59
60 #ifdef HAVE_UNISTD_H
61 # include <unistd.h>
62 #endif
63 #ifdef HAVE_DIRECT_H
64 # include <direct.h>
65 #endif
66 #ifdef HAVE_SYS_TYPES_H
67 # include <sys/types.h>
68 #endif
69 #ifdef HAVE_SYS_STAT_H
70 # include <sys/stat.h>
71 #endif
72 #ifdef HAVE_IO_H
73 # include <io.h>
74 #endif
75 #ifdef HAVE_SYS_UTIME_H
76 # include <sys/utime.h>
77 #endif
78 #ifdef HAVE_UTIME_H
79 # include <utime.h>
80 #endif
81 #include <fcntl.h>
82
83 #include "zip.h"
84 #include "unzip.h"
85
86 #ifdef WIN32
87 #define USEWIN32IOAPI
88 #include "iowin32.h"
89 #endif
90
91 #define WRITEBUFFERSIZE (16384)
92 #define MAXFILENAME (256)
93
94 using namespace std;
95 using lyx::support::FileName;
96
97
98 #ifdef WIN32
99 uLong filetime(const char * f, tm_zip * tmzip, uLong * dt)
100 {
101         int ret = 0;
102         {
103                 FILETIME ftLocal;
104                 HANDLE hFind;
105                 WIN32_FIND_DATA  ff32;
106
107                 hFind = FindFirstFile(f,&ff32);
108                 if (hFind != INVALID_HANDLE_VALUE)
109                 {
110                         FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
111                         FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
112                         FindClose(hFind);
113                         ret = 1;
114                 }
115         }
116         return ret;
117 }
118
119 #else
120 #ifdef unix
121 uLong filetime(const char * f, tm_zip * tmzip, uLong * /*dt*/)
122 {
123         int ret=0;
124         struct stat s;                                                            /* results of stat() */
125         struct tm* filedate;
126         time_t tm_t=0;
127
128         if (strcmp(f,"-")!=0) {
129                 char name[MAXFILENAME+1];
130                 int len = strlen(f);
131                 if (len > MAXFILENAME)
132                         len = MAXFILENAME;
133
134                 strncpy(name, f,MAXFILENAME-1);
135                 /* strncpy doesnt append the trailing NULL, of the string is too long. */
136                 name[ MAXFILENAME ] = '\0';
137
138                 if (name[len - 1] == '/')
139                         name[len - 1] = '\0';
140                 /* not all systems allow stat'ing a file with / appended */
141                 if (stat(name,&s)==0) {
142                         tm_t = s.st_mtime;
143                         ret = 1;
144                 }
145         }
146         filedate = localtime(&tm_t);
147
148         tmzip->tm_sec  = filedate->tm_sec;
149         tmzip->tm_min  = filedate->tm_min;
150         tmzip->tm_hour = filedate->tm_hour;
151         tmzip->tm_mday = filedate->tm_mday;
152         tmzip->tm_mon  = filedate->tm_mon ;
153         tmzip->tm_year = filedate->tm_year;
154
155         return ret;
156 }
157
158 #else
159
160 uLong filetime(const char * f, tm_zip * tmzip, uLong * dt)
161 {
162         return 0;
163 }
164 #endif
165 #endif
166
167 bool zipFiles(string const & zipfile, vector<pair<string, string> > const & files)
168 {
169         int err = 0;
170         zipFile zf;
171         int errclose;
172         void * buf = NULL;
173
174         int size_buf = WRITEBUFFERSIZE;
175         buf = (void*)malloc(size_buf);
176         if (buf==NULL) {
177                 printf("Error allocating memory\n");
178                 return false;
179         }
180         const char * fname = zipfile.c_str();
181
182 #ifdef USEWIN32IOAPI
183         zlib_filefunc_def ffunc;
184         fill_win32_filefunc(&ffunc);
185         // false: not append
186         zf = zipOpen2(fname, false, NULL, &ffunc);
187 #else
188         zf = zipOpen(fname, false);
189 #endif
190
191         if (zf == NULL) {
192                 return false;
193         }
194
195         for (vector<pair<string, string> >::const_iterator it = files.begin(); it != files.end(); ++it) {
196                 FILE * fin;
197                 int size_read;
198                 zip_fileinfo zi;
199                 const char * diskfilename = it->first.c_str();
200                 const char * filenameinzip = it->second.c_str();
201                 unsigned long crcFile=0;
202
203                 zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
204                         zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
205                 zi.dosDate = 0;
206                 zi.internal_fa = 0;
207                 zi.external_fa = 0;
208                 filetime(filenameinzip, &zi.tmz_date, &zi.dosDate);
209
210                 err = zipOpenNewFileInZip3(zf, filenameinzip, &zi,
211                         NULL,0,NULL,0,NULL /* comment*/,
212                         Z_DEFLATED,
213                         Z_DEFAULT_COMPRESSION,            // compression level
214                         0,
215                 /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
216                         -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
217                         NULL,                                             // password,
218                         crcFile);
219
220                 if (err != ZIP_OK) {
221                         printf("error in opening %s in zipfile\n",filenameinzip);
222                         return false;
223                 }
224                 fin = fopen(diskfilename, "rb");
225                 if (fin==NULL) {
226                         return false;
227                 }
228
229                 do {
230                         err = ZIP_OK;
231                         size_read = (int)fread(buf, 1, size_buf, fin);
232                         if (size_read < size_buf)
233                                 if (feof(fin)==0) {
234                                         printf("error in reading %s\n",filenameinzip);
235                                         return false;
236                                 }
237
238                         if (size_read>0) {
239                                 err = zipWriteInFileInZip (zf, buf, size_read);
240                                 if (err<0) {
241                                         printf("error in writing %s in the zipfile\n",
242                                                  filenameinzip);
243                                         return false;
244                                 }
245                         }
246                 } while ((err == ZIP_OK) && (size_read>0));
247
248                 if (fin)
249                         fclose(fin);
250
251                 err = zipCloseFileInZip(zf);
252                 if (err != ZIP_OK) {
253                         printf("error in closing %s in the zipfile\n",
254                                     filenameinzip);
255                         return false;
256                 }
257         }
258         errclose = zipClose(zf, NULL);
259         if (errclose != ZIP_OK) {
260                 printf("error in closing zip file\n");
261                 return false;
262         }
263         free(buf);
264         return true;
265 }
266
267 // adapted from miniunz.c
268
269 /* change_file_date : change the date/time of a file
270         filename : the filename of the file where date/time must be modified
271         dosdate : the new date at the MSDos format (4 bytes)
272         tmu_date : the SAME new date at the tm_unz format */
273 void change_file_date(const char * filename, uLong dosdate, tm_unz tmu_date)
274 {
275 #ifdef WIN32
276         HANDLE hFile;
277         FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
278
279         hFile = CreateFile(filename,GENERIC_READ | GENERIC_WRITE,
280                 0,NULL,OPEN_EXISTING,0,NULL);
281         GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
282         DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
283         LocalFileTimeToFileTime(&ftLocal,&ftm);
284         SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
285         CloseHandle(hFile);
286 #else
287 #ifdef unix
288         utimbuf ut;
289         tm newdate;
290
291         newdate.tm_sec = tmu_date.tm_sec;
292         newdate.tm_min=tmu_date.tm_min;
293         newdate.tm_hour=tmu_date.tm_hour;
294         newdate.tm_mday=tmu_date.tm_mday;
295         newdate.tm_mon=tmu_date.tm_mon;
296         if (tmu_date.tm_year > 1900)
297                 newdate.tm_year=tmu_date.tm_year - 1900;
298         else
299                 newdate.tm_year=tmu_date.tm_year ;
300         newdate.tm_isdst=-1;
301
302         ut.actime=ut.modtime=mktime(&newdate);
303         utime(filename,&ut);
304 #endif
305 #endif
306 }
307
308
309 int do_extract_currentfile(unzFile uf,
310         const int * popt_extract_without_path,
311         int * popt_overwrite,
312         const char * password,
313         const char * dirname)
314 {
315         char full_path[1024];
316         char* filename_inzip = full_path;
317         char* filename_withoutpath;
318         char* p;
319         int err=UNZ_OK;
320         FILE *fout=NULL;
321         void* buf;
322         uInt size_buf;
323
324         strcpy(full_path, dirname);
325         while ((*filename_inzip) != '\0') filename_inzip++;
326         if (*(filename_inzip-1) != '/' && *(filename_inzip-1) != '\\') {
327                 *filename_inzip = '/';
328                 *(++filename_inzip) = '\0';
329         }
330
331         unz_file_info file_info;
332         //uLong ratio=0;
333         err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,256,NULL,0,NULL,0);
334
335         if (err!=UNZ_OK) {
336                 printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
337                 return err;
338         }
339
340         size_buf = WRITEBUFFERSIZE;
341         buf = (void*)malloc(size_buf);
342         if (buf==NULL) {
343                 printf("Error allocating memory\n");
344                 return UNZ_INTERNALERROR;
345         }
346
347         p = filename_withoutpath = filename_inzip;
348         while ((*p) != '\0') {
349                 if (((*p)=='/') || ((*p)=='\\'))
350                         filename_withoutpath = p+1;
351                 p++;
352         }
353         // this is a directory
354         if ((*filename_withoutpath)=='\0') {
355                 if ((*popt_extract_without_path)==0) {
356                         printf("Create directory %s\n", filename_inzip);
357                         FileName(filename_inzip).createPath();
358                 }
359         }
360         // this is a filename
361         else {
362                 err = unzOpenCurrentFilePassword(uf,password);
363                 if (err!=UNZ_OK) {
364                         printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
365                 } else {
366                         fout=fopen(full_path, "wb");
367
368                         /* some zipfile don't contain directory alone before file */
369                         if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
370                                 (filename_withoutpath!=(char*)filename_inzip)) {
371                                 char c=*(filename_withoutpath-1);
372                                 *(filename_withoutpath-1)='\0';
373                                 printf("Create directory %s\n", full_path);
374                                 FileName(full_path).createPath();
375                                 *(filename_withoutpath-1)=c;
376                                 fout=fopen(full_path,"wb");
377                         }
378
379                         if (fout==NULL) {
380                                 printf("error opening %s\n", full_path);
381                         }
382                 }
383
384                 if (fout!=NULL) {
385
386                         do {
387                                 err = unzReadCurrentFile(uf,buf,size_buf);
388                                 if (err<0) {
389                                         printf("error %d with zipfile in unzReadCurrentFile\n",err);
390                                         break;
391                                 }
392                                 if (err>0)
393                                         if (fwrite(buf,err,1,fout)!=1) {
394                                                 printf("error in writing extracted file\n");
395                                                 err=UNZ_ERRNO;
396                                                 break;
397                                         }
398                         } while (err>0);
399                         if (fout)
400                                 fclose(fout);
401
402                         if (err==0)
403                                 change_file_date(full_path, file_info.dosDate,
404                                         file_info.tmu_date);
405                 }
406
407                 if (err==UNZ_OK) {
408                         err = unzCloseCurrentFile (uf);
409                         if (err!=UNZ_OK) {
410                                 printf("error %d with zipfile in unzCloseCurrentFile\n",err);
411                         }
412                 }
413                 else
414                         unzCloseCurrentFile(uf);          /* don't lose the error */
415         }
416
417         free(buf);
418         return err;
419 }
420
421
422 bool unzipToDir(string const & zipfile, string const & dirname)
423 {
424         unzFile uf=NULL;
425 #ifdef USEWIN32IOAPI
426         zlib_filefunc_def ffunc;
427 #endif
428
429         const char * zipfilename = zipfile.c_str();
430
431 #ifdef USEWIN32IOAPI
432         fill_win32_filefunc(&ffunc);
433         uf = unzOpen2(zipfilename, &ffunc);
434 #else
435         uf = unzOpen(zipfilename);
436 #endif
437
438         if (uf==NULL) {
439                 return false;
440         }
441
442         uLong i;
443         unz_global_info gi;
444         int err;
445         //FILE* fout=NULL;
446         int opt_extract_without_path = 0;
447         int opt_overwrite = 1;
448         char * password = NULL;
449
450         err = unzGetGlobalInfo (uf, &gi);
451         if (err != UNZ_OK) {
452                 printf("error %d with zipfile in unzGetGlobalInfo \n",err);
453                 return false;
454         }
455
456         for (i=0; i < gi.number_entry; i++) {
457                 if (do_extract_currentfile(uf, &opt_extract_without_path,
458                         &opt_overwrite,
459                         password, dirname.c_str()) != UNZ_OK)
460                         break;
461
462                 if ((i+1)<gi.number_entry) {
463                         err = unzGoToNextFile(uf);
464                         if (err != UNZ_OK) {
465                                 printf("error %d with zipfile in unzGoToNextFile\n",err);
466                                 break;
467                         }
468                 }
469         }
470
471         unzCloseCurrentFile(uf);
472     return true;
473 }
474