1 /*
2 ** zipdir.c
3 ** Copyright (C) 2008-2009 Randy Heit
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ****************************************************************************
20 **
21 ** Usage: zipdir [-dfuq] <zip file> <directory> ...
22 **
23 ** Given one or more directories, their contents are scanned recursively.
24 ** If any files are newer than the zip file or the zip file does not exist,
25 ** then everything in the specified directories is stored in the zip. The
26 ** base directory names are not stored in the zip file, but subdirectories
27 ** recursed into are stored.
28 */
29
30 // HEADER FILES ------------------------------------------------------------
31
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #ifdef _WIN32
35 #include <io.h>
36 #define stat _stat
37 #else
38 #include <dirent.h>
39 #if !defined(__sun)
40 #include <fts.h>
41 #endif
42 #endif
43 #include <stdio.h>
44 #include <string.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <stdlib.h>
48 #include <time.h>
49 #include "zlib.h"
50 #include "bzlib.h"
51 #include "LzmaEnc.h"
52 #include "7zVersion.h"
53 #ifdef PPMD
54 #include "../../ppmd/PPMd.h"
55 #endif
56
57 // MACROS ------------------------------------------------------------------
58
59 #ifndef _WIN32
60 #define __cdecl
61 #endif
62
63 #ifndef __BIG_ENDIAN__
64 #define MAKE_ID(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
65 #define LittleShort(x) (x)
66 #define LittleLong(x) (x)
67 #else
68 #define MAKE_ID(a,b,c,d) ((d)|((c)<<8)|((b)<<16)|((a)<<24))
LittleShort(unsigned short x)69 static unsigned short LittleShort(unsigned short x)
70 {
71 return (x>>8) | (x<<8);
72 }
73
LittleLong(unsigned int x)74 static unsigned int LittleLong(unsigned int x)
75 {
76 return (x>>24) | ((x>>8) & 0xff00) | ((x<<8) & 0xff0000) | (x<<24);
77 }
78 #endif
79
80 #define ZIP_LOCALFILE MAKE_ID('P','K',3,4)
81 #define ZIP_CENTRALFILE MAKE_ID('P','K',1,2)
82 #define ZIP_ENDOFDIR MAKE_ID('P','K',5,6)
83
84 #define METHOD_STORED 0
85 #define METHOD_DEFLATE 8
86 #define METHOD_BZIP2 12
87 #define METHOD_LZMA 14
88 #define METHOD_PPMD 98
89
90 // Buffer size for central directory search
91 #define BUFREADCOMMENT (0x400)
92
93 #ifndef PATH_MAX
94 #define PATH_MAX 4096
95 #endif
96
97 // TYPES -------------------------------------------------------------------
98
99 typedef struct file_entry_s
100 {
101 struct file_entry_s *next;
102 time_t time_write;
103 unsigned int uncompressed_size;
104 unsigned int compressed_size;
105 unsigned int crc32;
106 unsigned int zip_offset;
107 short date, time;
108 short method;
109 char path[];
110 } file_entry_t;
111
112 typedef struct dir_tree_s
113 {
114 struct dir_tree_s *next;
115 file_entry_t *files;
116 size_t path_size;
117 char path[];
118 } dir_tree_t;
119
120 typedef struct file_sorted_s
121 {
122 file_entry_t *file;
123 char *path_in_zip;
124 } file_sorted_t;
125
126 typedef struct compressor_s
127 {
128 int (*compress)(Byte *out, unsigned int *outlen, const Byte *in, unsigned int inlen);
129 int method;
130 } compressor_t;
131
132 typedef unsigned int UINT32;
133 typedef unsigned short WORD;
134 typedef unsigned char BYTE;
135
136 // [BL] Solaris (well GCC on Solaris) doesn't seem to support pack(push/pop, 1) so we'll need to use use it
137 // on the whole file.
138 #pragma pack(1)
139 //#pragma pack(push,1)
140 typedef struct
141 {
142 UINT32 Magic; // 0
143 BYTE VersionToExtract[2]; // 4
144 WORD Flags; // 6
145 WORD Method; // 8
146 WORD ModTime; // 10
147 WORD ModDate; // 12
148 UINT32 CRC32; // 14
149 UINT32 CompressedSize; // 18
150 UINT32 UncompressedSize; // 22
151 WORD NameLength; // 26
152 WORD ExtraLength; // 28
153 } LocalFileHeader;
154
155 typedef struct
156 {
157 UINT32 Magic;
158 BYTE VersionMadeBy[2];
159 BYTE VersionToExtract[2];
160 WORD Flags;
161 WORD Method;
162 WORD ModTime;
163 WORD ModDate;
164 UINT32 CRC32;
165 UINT32 CompressedSize;
166 UINT32 UncompressedSize;
167 WORD NameLength;
168 WORD ExtraLength;
169 WORD CommentLength;
170 WORD StartingDiskNumber;
171 WORD InternalAttributes;
172 UINT32 ExternalAttributes;
173 UINT32 LocalHeaderOffset;
174 } CentralDirectoryEntry;
175
176 typedef struct
177 {
178 UINT32 Magic;
179 WORD DiskNumber;
180 WORD FirstDisk;
181 WORD NumEntries;
182 WORD NumEntriesOnAllDisks;
183 UINT32 DirectorySize;
184 UINT32 DirectoryOffset;
185 WORD ZipCommentLength;
186 } EndOfCentralDirectory;
187 //#pragma pack(pop)
188
189 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
190
191 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
192
193 void print_usage(const char *cmdname);
194 dir_tree_t *alloc_dir_tree(const char *dir);
195 file_entry_t *alloc_file_entry(const char *prefix, const char *path, time_t last_written);
196 void free_dir_tree(dir_tree_t *tree);
197 void free_dir_trees(dir_tree_t *tree);
198 #ifdef _WIN32
199 void recurse_dir(dir_tree_t *tree, const char *dirpath);
200 dir_tree_t *add_dir(const char *dirpath);
201 #endif
202 dir_tree_t *add_dirs(char **argv);
203 int count_files(dir_tree_t *trees);
204 int __cdecl sort_cmp(const void *a, const void *b);
205 file_sorted_t *sort_files(dir_tree_t *trees, int num_files);
206 void write_zip(const char *zipname, dir_tree_t *trees, int update);
207 int append_to_zip(FILE *zip_file, file_sorted_t *file, FILE *ozip, BYTE *odir);
208 int write_central_dir(FILE *zip, file_sorted_t *file);
209 void time_to_dos(struct tm *time, short *dosdate, short *dostime);
210 int method_to_version(int method);
211 const char *method_name(int method);
212 int compress_lzma(Byte *out, unsigned int *outlen, const Byte *in, unsigned int inlen);
213 int compress_bzip2(Byte *out, unsigned int *outlen, const Byte *in, unsigned int inlen);
214 int compress_ppmd(Byte *out, unsigned int *outlen, const Byte *in, unsigned int inlen);
215 int compress_deflate(Byte *out, unsigned int *outlen, const Byte *in, unsigned int inlen);
216 BYTE *find_central_dir(FILE *fin);
217 CentralDirectoryEntry *find_file_in_zip(BYTE *dir, const char *path, unsigned int len, unsigned int crc, short date, short time);
218 int copy_zip_file(FILE *zip, file_entry_t *file, FILE *ozip, CentralDirectoryEntry *dirent);
219
220 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
221
SzAlloc(void * p,size_t size)222 static void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); }
SzFree(void * p,void * address)223 static void SzFree(void *p, void *address) { p = p; free(address); }
224
225 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
226
227 // PUBLIC DATA DEFINITIONS -------------------------------------------------
228
229 int DeflateOnly;
230 int UpdateCount;
231 int Quiet;
232
233 // PRIVATE DATA DEFINITIONS ------------------------------------------------
234
235 static int no_mem;
236
237 static ISzAlloc Alloc = { SzAlloc, SzFree };
238 static compressor_t Compressors[] =
239 {
240 { compress_lzma, METHOD_LZMA },
241 { compress_bzip2, METHOD_BZIP2 },
242 #ifdef PPMD
243 { compress_ppmd, METHOD_PPMD },
244 #endif
245 { compress_deflate, METHOD_DEFLATE },
246 { NULL, 0 }
247 };
248
249 // CODE --------------------------------------------------------------------
250
251 //==========================================================================
252 //
253 // print_usage
254 //
255 //==========================================================================
256
print_usage(const char * cmdname)257 void print_usage(const char *cmdname)
258 {
259 #ifdef _WIN32
260 const char *rchar = strrchr(cmdname, '\\');
261 if (rchar != NULL)
262 {
263 cmdname = rchar+1;
264 }
265 #endif
266 fprintf(stderr, "Usage: %s [options] <zip file> <directory> ...\n"
267 "Options: -d Use deflate compression only\n"
268 " -f Force creation of archive\n"
269 " -u Only update changed files\n"
270 " -q Do not list files\n", cmdname);
271 }
272
273 //==========================================================================
274 //
275 // alloc_dir_tree
276 //
277 //==========================================================================
278
alloc_dir_tree(const char * dir)279 dir_tree_t *alloc_dir_tree(const char *dir)
280 {
281 dir_tree_t *tree;
282 size_t dirlen;
283
284 dirlen = strlen(dir);
285 tree = malloc(sizeof(dir_tree_t) + dirlen + 2);
286 if (tree != NULL)
287 {
288 strcpy(tree->path, dir);
289 tree->path_size = dirlen;
290 if (dir[dirlen - 1] != '/')
291 {
292 tree->path_size++;
293 tree->path[dirlen] = '/';
294 tree->path[dirlen + 1] = '\0';
295 }
296 tree->files = NULL;
297 tree->next = NULL;
298 }
299 return tree;
300 }
301
302 //==========================================================================
303 //
304 // alloc_file_entry
305 //
306 //==========================================================================
307
alloc_file_entry(const char * prefix,const char * path,time_t last_written)308 file_entry_t *alloc_file_entry(const char *prefix, const char *path, time_t last_written)
309 {
310 file_entry_t *entry;
311
312 entry = malloc(sizeof(file_entry_t) + strlen(prefix) + strlen(path) + 1);
313 if (entry != NULL)
314 {
315 strcpy(entry->path, prefix);
316 strcat(entry->path, path);
317 entry->next = NULL;
318 entry->time_write = last_written;
319 }
320 return entry;
321 }
322
323 //==========================================================================
324 //
325 // free_dir_tree
326 //
327 //==========================================================================
328
free_dir_tree(dir_tree_t * tree)329 void free_dir_tree(dir_tree_t *tree)
330 {
331 file_entry_t *entry, *next;
332
333 if (tree != NULL)
334 {
335 for (entry = tree->files; entry != NULL; entry = next)
336 {
337 next = entry->next;
338 free(entry);
339 }
340 free(tree);
341 }
342 }
343
344 //==========================================================================
345 //
346 // free_dir_trees
347 //
348 //==========================================================================
349
free_dir_trees(dir_tree_t * tree)350 void free_dir_trees(dir_tree_t *tree)
351 {
352 dir_tree_t *next;
353
354 for (; tree != NULL; tree = next)
355 {
356 next = tree->next;
357 free_dir_tree(tree);
358 }
359 }
360
361 #ifdef _WIN32
362
363 //==========================================================================
364 //
365 // recurse_dir
366 //
367 //==========================================================================
368
recurse_dir(dir_tree_t * tree,const char * dirpath)369 void recurse_dir(dir_tree_t *tree, const char *dirpath)
370 {
371 struct _finddata_t fileinfo;
372 intptr_t handle;
373 char *dirmatch;
374
375 dirmatch = malloc(strlen(dirpath) + 2);
376 if (dirmatch == NULL)
377 {
378 no_mem = 1;
379 return;
380 }
381 strcpy(dirmatch, dirpath);
382 strcat(dirmatch, "*");
383 if ((handle = _findfirst(dirmatch, &fileinfo)) == -1)
384 {
385 fprintf(stderr, "Could not scan '%s': %s\n", dirpath, strerror(errno));
386 }
387 else
388 {
389 do
390 {
391 if (fileinfo.attrib & _A_HIDDEN)
392 {
393 // Skip hidden files and directories. (Prevents SVN bookkeeping
394 // info from being included.)
395 continue;
396 }
397 if (fileinfo.attrib & _A_SUBDIR)
398 {
399 char *newdir;
400
401 if (fileinfo.name[0] == '.' &&
402 (fileinfo.name[1] == '\0' ||
403 (fileinfo.name[1] == '.' && fileinfo.name[2] == '\0')))
404 {
405 // Do not record . and .. directories.
406 continue;
407 }
408 newdir = malloc(strlen(dirpath) + strlen(fileinfo.name) + 2);
409 strcpy(newdir, dirpath);
410 strcat(newdir, fileinfo.name);
411 strcat(newdir, "/");
412 recurse_dir(tree, newdir);
413 }
414 else
415 {
416 file_entry_t *entry;
417
418 if (strstr(fileinfo.name, ".orig"))
419 {
420 // .orig files are left behind by patch.exe and should never be
421 // added to zdoom.pk3
422 continue;
423 }
424
425 entry = alloc_file_entry(dirpath, fileinfo.name, fileinfo.time_write);
426 if (entry == NULL)
427 {
428 no_mem = 1;
429 break;
430 }
431 entry->next = tree->files;
432 tree->files = entry;
433 }
434 } while (_findnext(handle, &fileinfo) == 0);
435 _findclose(handle);
436 }
437 free(dirmatch);
438 }
439
440 //==========================================================================
441 //
442 // add_dir
443 //
444 //==========================================================================
445
add_dir(const char * dirpath)446 dir_tree_t *add_dir(const char *dirpath)
447 {
448 dir_tree_t *tree = alloc_dir_tree(dirpath);
449
450 if (tree != NULL)
451 {
452 recurse_dir(tree, tree->path);
453 }
454 return tree;
455 }
456
457 //==========================================================================
458 //
459 // add_dirs
460 // Windows version
461 //
462 // Given NULL-terminated array of directory paths, create trees for them.
463 //
464 //==========================================================================
465
add_dirs(char ** argv)466 dir_tree_t *add_dirs(char **argv)
467 {
468 dir_tree_t *tree, *trees = NULL;
469 char *s;
470
471 while (*argv != NULL)
472 {
473 for (s = *argv; *s != '\0'; ++s)
474 {
475 if (*s == '\\')
476 {
477 *s = '/';
478 }
479 }
480 tree = add_dir(*argv);
481 tree->next = trees;
482 trees = tree;
483 if (no_mem)
484 {
485 break;
486 }
487 argv++;
488 }
489 return trees;
490 }
491
492 #elif defined(__sun)
493
494 //==========================================================================
495 //
496 // add_dirs
497 // Solaris version
498 //
499 // Given NULL-terminated array of directory paths, create trees for them.
500 //
501 //==========================================================================
502
add_dir(dir_tree_t * tree,char * dirpath)503 void add_dir(dir_tree_t *tree, char* dirpath)
504 {
505 DIR *directory = opendir(dirpath);
506 if(directory == NULL)
507 return;
508
509 struct dirent *file;
510 while((file = readdir(directory)) != NULL)
511 {
512 if(file->d_name[0] == '.') //File is hidden or ./.. directory so ignore it.
513 continue;
514
515 int isDirectory = 0;
516 int time = 0;
517
518 char* fullFileName = malloc(strlen(dirpath) + strlen(file->d_name) + 1);
519 strcpy(fullFileName, dirpath);
520 strcat(fullFileName, file->d_name);
521
522 struct stat *fileStat;
523 fileStat = malloc(sizeof(struct stat));
524 stat(fullFileName, fileStat);
525 isDirectory = S_ISDIR(fileStat->st_mode);
526 time = fileStat->st_mtime;
527 free(stat);
528
529 free(fullFileName);
530
531 if(isDirectory)
532 {
533 char* newdir;
534 newdir = malloc(strlen(dirpath) + strlen(file->d_name) + 2);
535 strcpy(newdir, dirpath);
536 strcat(newdir, file->d_name);
537 strcat(newdir, "/");
538 add_dir(tree, newdir);
539 free(newdir);
540 continue;
541 }
542
543 file_entry_t *entry;
544 entry = alloc_file_entry(dirpath, file->d_name, time);
545 if (entry == NULL)
546 {
547 //no_mem = 1;
548 break;
549 }
550 entry->next = tree->files;
551 tree->files = entry;
552 }
553
554 closedir(directory);
555 }
556
add_dirs(char ** argv)557 dir_tree_t *add_dirs(char **argv)
558 {
559 dir_tree_t *tree, *trees = NULL;
560
561 int i = 0;
562 while(argv[i] != NULL)
563 {
564 tree = alloc_dir_tree(argv[i]);
565 tree->next = trees;
566 trees = tree;
567
568 if(tree != NULL)
569 {
570 char* dirpath = malloc(sizeof(argv[i]) + 2);
571 strcpy(dirpath, argv[i]);
572 if(dirpath[strlen(dirpath)] != '/')
573 strcat(dirpath, "/");
574 add_dir(tree, dirpath);
575 free(dirpath);
576 }
577
578 i++;
579 }
580 return trees;
581 }
582
583 #else
584
585 //==========================================================================
586 //
587 // add_dirs
588 // 4.4BSD version
589 //
590 // Given NULL-terminated array of directory paths, create trees for them.
591 //
592 //==========================================================================
593
add_dirs(char ** argv)594 dir_tree_t *add_dirs(char **argv)
595 {
596 FTS *fts;
597 FTSENT *ent;
598 dir_tree_t *tree, *trees = NULL;
599 file_entry_t *file;
600
601 fts = fts_open(argv, FTS_LOGICAL, NULL);
602 if (fts == NULL)
603 {
604 fprintf(stderr, "Failed to start directory traversal: %s\n", strerror(errno));
605 return NULL;
606 }
607 while ((ent = fts_read(fts)) != NULL)
608 {
609 if (ent->fts_info == FTS_D && ent->fts_name[0] == '.')
610 {
611 // Skip hidden directories. (Prevents SVN bookkeeping
612 // info from being included.)
613 // [BL] Also skip backup files.
614 fts_set(fts, ent, FTS_SKIP);
615 }
616 if (ent->fts_info == FTS_D && ent->fts_level == 0)
617 {
618 tree = alloc_dir_tree(ent->fts_path);
619 if (tree == NULL)
620 {
621 no_mem = 1;
622 break;
623 }
624 tree->next = trees;
625 trees = tree;
626 }
627 if (ent->fts_info != FTS_F)
628 {
629 // We're only interested in remembering files.
630 continue;
631 }
632 else if(ent->fts_name[strlen(ent->fts_name)-1] == '~')
633 {
634 // Don't remember backup files.
635 continue;
636 }
637 file = alloc_file_entry("", ent->fts_path, ent->fts_statp->st_mtime);
638 if (file == NULL)
639 {
640 no_mem = 1;
641 break;
642 }
643 file->next = tree->files;
644 tree->files = file;
645 }
646 fts_close(fts);
647 return trees;
648 }
649 #endif
650
651 //==========================================================================
652 //
653 // count_files
654 //
655 //==========================================================================
656
count_files(dir_tree_t * trees)657 int count_files(dir_tree_t *trees)
658 {
659 dir_tree_t *tree;
660 file_entry_t *file;
661 int count;
662
663 for (count = 0, tree = trees; tree != NULL; tree = tree->next)
664 {
665 for (file = tree->files; file != NULL; file = file->next)
666 {
667 count++;
668 }
669 }
670 return count;
671 }
672
673 //==========================================================================
674 //
675 // sort_cmp
676 //
677 // Arbitrarily-selected sorting for the zip files: Files in the root
678 // directory sort after files in subdirectories. Otherwise, everything
679 // sorts by name.
680 //
681 //==========================================================================
682
sort_cmp(const void * a,const void * b)683 int __cdecl sort_cmp(const void *a, const void *b)
684 {
685 const file_sorted_t *sort1 = (const file_sorted_t *)a;
686 const file_sorted_t *sort2 = (const file_sorted_t *)b;
687 int in_dir1, in_dir2;
688
689 in_dir1 = (strchr(sort1->path_in_zip, '/') != NULL);
690 in_dir2 = (strchr(sort2->path_in_zip, '/') != NULL);
691 if (in_dir1 == 1 && in_dir2 == 0)
692 {
693 return -1;
694 }
695 if (in_dir1 == 0 && in_dir2 == 1)
696 {
697 return 1;
698 }
699 return strcmp(((const file_sorted_t *)a)->path_in_zip,
700 ((const file_sorted_t *)b)->path_in_zip);
701 }
702
703 //==========================================================================
704 //
705 // sort_files
706 //
707 //==========================================================================
708
sort_files(dir_tree_t * trees,int num_files)709 file_sorted_t *sort_files(dir_tree_t *trees, int num_files)
710 {
711 file_sorted_t *sorter;
712 dir_tree_t *tree;
713 file_entry_t *file;
714 int i;
715
716 sorter = malloc(sizeof(*sorter) * num_files);
717 if (sorter != NULL)
718 {
719 for (i = 0, tree = trees; tree != NULL; tree = tree->next)
720 {
721 for (file = tree->files; file != NULL; file = file->next)
722 {
723 sorter[i].file = file;
724 sorter[i].path_in_zip = file->path + tree->path_size;
725 i++;
726 }
727 }
728 qsort(sorter, num_files, sizeof(*sorter), sort_cmp);
729 }
730 return sorter;
731 }
732
733 //==========================================================================
734 //
735 // write_zip
736 //
737 //==========================================================================
738
write_zip(const char * zipname,dir_tree_t * trees,int update)739 void write_zip(const char *zipname, dir_tree_t *trees, int update)
740 {
741 #ifdef _WIN32
742 char tempname[_MAX_PATH];
743 #else
744 char tempname[PATH_MAX];
745 #endif
746 EndOfCentralDirectory dirend;
747 int i, num_files;
748 file_sorted_t *sorted;
749 FILE *zip, *ozip = NULL;
750 void *central_dir = NULL;
751
752 num_files = count_files(trees);
753 sorted = sort_files(trees, num_files);
754 if (sorted == NULL)
755 {
756 no_mem = 1;
757 return;
758 }
759 if (update)
760 {
761 sprintf(tempname, "%s.temp", zipname);
762 ozip = fopen(zipname, "rb");
763 if (ozip == NULL)
764 {
765 fprintf(stderr, "Could not open %s for updating: %s\n", zipname, strerror(errno));
766 update = 0;
767 }
768 else
769 {
770 central_dir = find_central_dir(ozip);
771 if (central_dir == NULL)
772 {
773 fprintf(stderr, "Could not read central directory from %s. (Is it a zipfile?)\n", zipname);
774 update = 0;
775 }
776 }
777 if (!update)
778 {
779 fprintf(stderr, "Will proceed as if -u had not been specified.\n");
780 }
781 }
782 if (update)
783 {
784 zip = fopen(tempname, "wb");
785 }
786 else
787 {
788 zip = fopen(zipname, "wb");
789 }
790 if (zip == NULL)
791 {
792 fprintf(stderr, "Could not open %s: %s\n", zipname, strerror(errno));
793 }
794 else
795 {
796 // Write each file.
797 for (i = 0; i < num_files; ++i)
798 {
799 if (append_to_zip(zip, sorted + i, ozip, central_dir))
800 {
801 break;
802 }
803 }
804 if (i == num_files)
805 {
806 // Write central directory.
807 dirend.DirectoryOffset = ftell(zip);
808 for (i = 0; i < num_files; ++i)
809 {
810 write_central_dir(zip, sorted + i);
811 }
812 // Write the directory terminator.
813 dirend.Magic = ZIP_ENDOFDIR;
814 dirend.DiskNumber = 0;
815 dirend.FirstDisk = 0;
816 dirend.NumEntriesOnAllDisks = dirend.NumEntries = LittleShort(i);
817 dirend.DirectorySize = LittleLong(ftell(zip) - dirend.DirectoryOffset);
818 dirend.DirectoryOffset = LittleLong(dirend.DirectoryOffset);
819 dirend.ZipCommentLength = 0;
820 if (fwrite(&dirend, sizeof(dirend), 1, zip) != 1)
821 {
822 fprintf(stderr, "Failed writing zip directory terminator: %s\n", strerror(errno));
823 }
824 printf("%s contains %d files (updated %d)\n", zipname, num_files, UpdateCount);
825 fclose(zip);
826
827 if (ozip != NULL)
828 {
829 // Delete original, and rename temp to take its place
830 fclose(ozip);
831 ozip = NULL;
832 if (remove(zipname))
833 {
834 fprintf(stderr, "Could not delete old zip: %s\nUpdated zip can be found at %s\n",
835 strerror(errno), tempname);
836 }
837 else if (rename(tempname, zipname))
838 {
839 fprintf(stderr, "Could not rename %s to %s: %s\n",
840 tempname, zipname, strerror(errno));
841 }
842 }
843 }
844 }
845 free(sorted);
846 if (ozip != NULL)
847 {
848 fclose(ozip);
849 }
850 if (central_dir != NULL)
851 {
852 free(central_dir);
853 }
854 }
855
856 //==========================================================================
857 //
858 // append_to_zip
859 //
860 // Write a given file to the zipFile.
861 //
862 // zipfile: zip object to be written to
863 // file: file to read data from
864 //
865 // returns: 0 = success, 1 = error
866 //
867 //==========================================================================
868
append_to_zip(FILE * zip_file,file_sorted_t * filep,FILE * ozip,BYTE * odir)869 int append_to_zip(FILE *zip_file, file_sorted_t *filep, FILE *ozip, BYTE *odir)
870 {
871 LocalFileHeader local;
872 uLong crc;
873 file_entry_t *file;
874 Byte *readbuf;
875 Byte *compbuf[2];
876 unsigned int comp_len[2];
877 int offset[2];
878 int method[2];
879 int best;
880 int slot;
881 FILE *lumpfile;
882 unsigned int readlen;
883 unsigned int len;
884 int i;
885 struct tm *ltime;
886
887 file = filep->file;
888
889 // try to determine local time
890 ltime = localtime(&file->time_write);
891 time_to_dos(ltime, &file->date, &file->time);
892
893 // lumpfile = source file
894 lumpfile = fopen(file->path, "rb");
895 if (lumpfile == NULL)
896 {
897 fprintf(stderr, "Could not open %s: %s\n", file->path, strerror(errno));
898 return 1;
899 }
900 // len = source size
901 fseek (lumpfile, 0, SEEK_END);
902 len = ftell(lumpfile);
903 fseek (lumpfile, 0, SEEK_SET);
904
905 // allocate a buffer for the whole source file
906 readbuf = malloc(len);
907 if (readbuf == NULL)
908 {
909 fclose(lumpfile);
910 fprintf(stderr, "Could not allocate %u bytes\n", (int)len);
911 return 1;
912 }
913 // read the whole source file into buffer
914 readlen = (unsigned int)fread(readbuf, 1, len, lumpfile);
915 fclose(lumpfile);
916
917 // if read less bytes than expected,
918 if (readlen != len)
919 {
920 // diagnose and return error
921 free(readbuf);
922 fprintf(stderr, "Unable to read %s\n", file->path);
923 return 1;
924 }
925 // file loaded
926
927 file->uncompressed_size = len;
928 file->compressed_size = len;
929 file->method = METHOD_STORED;
930
931 // Calculate CRC32 for file.
932 crc = crc32(0, NULL, 0);
933 crc = crc32(crc, readbuf, (uInt)len);
934 file->crc32 = LittleLong(crc);
935
936 // Can we save time and just copy the file from the old zip?
937 if (odir != NULL && ozip != NULL)
938 {
939 CentralDirectoryEntry *dirent;
940
941 dirent = find_file_in_zip(odir, filep->path_in_zip, len, crc, file->date, file->time);
942 if (dirent != NULL)
943 {
944 i = copy_zip_file(zip_file, file, ozip, dirent);
945 if (i > 0)
946 {
947 free(readbuf);
948 return 0;
949 }
950 if (i < 0)
951 {
952 free(readbuf);
953 fprintf(stderr, "Unable to write %s to zip\n", file->path);
954 return 1;
955 }
956 }
957 }
958
959 if (!Quiet)
960 {
961 if (ozip != NULL)
962 {
963 printf("Updating %-40s", filep->path_in_zip);
964 }
965 else
966 {
967 printf("Adding %-40s", filep->path_in_zip);
968 }
969 }
970 UpdateCount++;
971
972 // Allocate a buffer for compression, one byte less than the source buffer.
973 // If it doesn't fit in that space, then skip compression and store it as-is.
974 compbuf[0] = malloc(len - 1);
975 compbuf[1] = malloc(len - 1);
976 best = -1; // best slot
977 slot = 0; // slot we are compressing to now
978
979 // Find best compression method. We have two output buffers. One to hold the
980 // best compression method, and the other to hold the compression we are trying
981 // now.
982 for (i = 0; Compressors[i].compress != NULL; ++i)
983 {
984 if (DeflateOnly && Compressors[i].method != METHOD_DEFLATE)
985 {
986 continue;
987 }
988 comp_len[slot] = len - 1;
989 method[slot] = Compressors[i].method;
990 offset[slot] = Compressors[i].compress(compbuf[slot], &comp_len[slot], readbuf, len);
991 if (offset[slot] >= 0)
992 {
993 if (best < 0 || comp_len[slot] <= comp_len[best])
994 {
995 best = slot;
996 slot ^= 1;
997 }
998 }
999 }
1000
1001 if (best >= 0)
1002 {
1003 file->method = method[best];
1004 file->compressed_size = comp_len[best];
1005 }
1006 // printf("%s -> method %d -> slot %d\n", filep->path_in_zip, file->method, best);
1007
1008 // Fill in local directory header.
1009 local.Magic = ZIP_LOCALFILE;
1010 local.VersionToExtract[0] = method_to_version(file->method);
1011 local.VersionToExtract[1] = 0;
1012 local.Flags = file->method == METHOD_DEFLATE ? LittleShort(2) : 0;
1013 local.Method = LittleShort(file->method);
1014 local.ModTime = file->time;
1015 local.ModDate = file->date;
1016 local.CRC32 = file->crc32;
1017 local.UncompressedSize = LittleLong(file->uncompressed_size);
1018 local.CompressedSize = LittleLong(file->compressed_size);
1019 local.NameLength = LittleShort((unsigned short)strlen(filep->path_in_zip));
1020 local.ExtraLength = 0;
1021
1022 file->zip_offset = ftell(zip_file);
1023
1024 // Write out the header, file name, and file data.
1025 if (fwrite(&local, sizeof(local), 1, zip_file) != 1 ||
1026 fwrite(filep->path_in_zip, strlen(filep->path_in_zip), 1, zip_file) != 1 ||
1027 (file->method ? fwrite(compbuf[best] + offset[best], 1, comp_len[best], zip_file) != comp_len[best] :
1028 fwrite(readbuf, 1, len, zip_file) != len))
1029 {
1030 if (!Quiet)
1031 {
1032 printf("\n");
1033 }
1034 fprintf(stderr, "Unable to write %s to zip\n", file->path);
1035 free(readbuf);
1036 if (compbuf[0] != NULL)
1037 {
1038 free(compbuf[0]);
1039 }
1040 if (compbuf[1] != NULL)
1041 {
1042 free(compbuf[1]);
1043 }
1044 return 1;
1045 }
1046
1047 // all done
1048 free(readbuf);
1049 if (compbuf[0] != NULL)
1050 {
1051 free(compbuf[0]);
1052 }
1053 if (compbuf[1] != NULL)
1054 {
1055 free(compbuf[1]);
1056 }
1057 if (!Quiet)
1058 {
1059 printf("%5.1f%% [%6u/%6u] %s\n", 100.0 - 100.0 * file->compressed_size / file->uncompressed_size,
1060 file->compressed_size, file->uncompressed_size, method_name(file->method));
1061 }
1062 return 0;
1063 }
1064
1065 //==========================================================================
1066 //
1067 // write_central_dir
1068 //
1069 // Writes the central directory entry for a file.
1070 //
1071 //==========================================================================
1072
write_central_dir(FILE * zip,file_sorted_t * filep)1073 int write_central_dir(FILE *zip, file_sorted_t *filep)
1074 {
1075 CentralDirectoryEntry dir;
1076 file_entry_t *file;
1077
1078 file = filep->file;
1079 dir.Magic = ZIP_CENTRALFILE;
1080 dir.VersionMadeBy[0] = 20;
1081 dir.VersionMadeBy[1] = 0;
1082 dir.VersionToExtract[0] = method_to_version(file->method);
1083 dir.VersionToExtract[1] = 0;
1084 dir.Flags = file->method == METHOD_DEFLATE ? LittleShort(2) : 0;
1085 dir.Method = LittleShort(file->method);
1086 dir.ModTime = file->time;
1087 dir.ModDate = file->date;
1088 dir.CRC32 = file->crc32;
1089 dir.CompressedSize = LittleLong(file->compressed_size);
1090 dir.UncompressedSize = LittleLong(file->uncompressed_size);
1091 dir.NameLength = LittleShort((unsigned short)strlen(filep->path_in_zip));
1092 dir.ExtraLength = 0;
1093 dir.CommentLength = 0;
1094 dir.StartingDiskNumber = 0;
1095 dir.InternalAttributes = 0;
1096 dir.ExternalAttributes = 0;
1097 dir.LocalHeaderOffset = LittleLong(file->zip_offset);
1098
1099 if (fwrite(&dir, sizeof(dir), 1, zip) != 1 ||
1100 fwrite(filep->path_in_zip, strlen(filep->path_in_zip), 1, zip) != 1)
1101 {
1102 fprintf(stderr, "Error writing central directory header for %s: %s\n", file->path, strerror(errno));
1103 return 1;
1104 }
1105 return 0;
1106 }
1107
1108 //==========================================================================
1109 //
1110 // time_to_dos
1111 //
1112 // Converts time from struct tm to the DOS format used by zip files.
1113 //
1114 //==========================================================================
1115
time_to_dos(struct tm * time,short * dosdate,short * dostime)1116 void time_to_dos(struct tm *time, short *dosdate, short *dostime)
1117 {
1118 if (time == NULL || time->tm_year < 80)
1119 {
1120 *dosdate = *dostime = 0;
1121 }
1122 else
1123 {
1124 *dosdate = LittleShort((time->tm_year - 80) * 512 + (time->tm_mon + 1) * 32 + time->tm_mday);
1125 *dostime = LittleShort(time->tm_hour * 2048 + time->tm_min * 32 + time->tm_sec / 2);
1126 }
1127 }
1128
1129 //==========================================================================
1130 //
1131 // method_to_version
1132 //
1133 // Given a compression method, returns the version of the ZIP appnote
1134 // required to decompress it, for filling in the directory information.
1135 //
1136 //==========================================================================
1137
method_to_version(int method)1138 int method_to_version(int method)
1139 {
1140 // Apparently, real-world programs get confused by setting the version
1141 // to extract field to something other than 2.0.
1142 #if 0
1143 if (method == METHOD_LZMA || method == METHOD_PPMD)
1144 return 63;
1145 if (method == METHOD_BZIP2)
1146 return 46;
1147 #endif
1148 // Default anything else to PKZIP 2.0.
1149 return 20;
1150 }
1151
1152 //==========================================================================
1153 //
1154 // method_name
1155 //
1156 // Returns the name of the compression method. If the method is unknown,
1157 // this will point to a static buffer.
1158 //
1159 //==========================================================================
1160
method_name(int method)1161 const char *method_name(int method)
1162 {
1163 static char unkn[16];
1164
1165 if (method == METHOD_STORED)
1166 {
1167 return "Stored";
1168 }
1169 if (method == METHOD_DEFLATE)
1170 {
1171 return "Deflate";
1172 }
1173 if (method == METHOD_LZMA)
1174 {
1175 return "LZMA";
1176 }
1177 if (method == METHOD_PPMD)
1178 {
1179 return "PPMd";
1180 }
1181 if (method == METHOD_BZIP2)
1182 {
1183 return "BZip2";
1184 }
1185 sprintf(unkn, "Unk:%03d", method);
1186 return unkn;
1187 }
1188
1189 //==========================================================================
1190 //
1191 // compress_lzma
1192 //
1193 // Returns non-negative offset to start of data stream on success. Barring
1194 // any strange changes to the LZMA library in the future, success should
1195 // always return 0.
1196 //
1197 //==========================================================================
1198
compress_lzma(Byte * out,unsigned int * outlen,const Byte * in,unsigned int inlen)1199 int compress_lzma(Byte *out, unsigned int *outlen, const Byte *in, unsigned int inlen)
1200 {
1201 CLzmaEncProps lzma_props;
1202 size_t props_size;
1203 size_t comp_len;
1204 int offset;
1205
1206 if (*outlen < 1 + 4 + LZMA_PROPS_SIZE)
1207 {
1208 // Not enough room for LZMA properties header + compressed data.
1209 return -1;
1210 }
1211 if (out == NULL || in == NULL || inlen == 0)
1212 {
1213 return -1;
1214 }
1215
1216 LzmaEncProps_Init(&lzma_props);
1217 // lzma_props.level = 9;
1218 props_size = LZMA_PROPS_SIZE;
1219 comp_len = *outlen - 4 - LZMA_PROPS_SIZE;
1220
1221 if (SZ_OK != LzmaEncode(out + 4 + LZMA_PROPS_SIZE, &comp_len, in, inlen, &lzma_props,
1222 out + 4, &props_size, 0, NULL, &Alloc, &Alloc))
1223 {
1224 return -1;
1225 }
1226 // Fill in LZMA properties header
1227 offset = 0;
1228 if (props_size != LZMA_PROPS_SIZE)
1229 {
1230 // Move LZMA properties to be adjacent to the compressed data, because for
1231 // some reaseon the library didn't use all the space provided.
1232 int i;
1233
1234 offset = (int)(LZMA_PROPS_SIZE - props_size);
1235 for (i = 4 + LZMA_PROPS_SIZE - 1; i > 4 + offset; --i)
1236 {
1237 out[i] = out[i - offset];
1238 }
1239 }
1240 out[offset] = MY_VER_MAJOR;
1241 out[offset+1] = MY_VER_MINOR;
1242 out[offset+2] = (Byte)props_size;
1243 out[offset+3] = 0;
1244 // Add header length to outlen
1245 *outlen = (unsigned int)(comp_len + 4 + props_size);
1246 return offset;
1247 }
1248
1249 //==========================================================================
1250 //
1251 // compress_bzip2
1252 //
1253 // Returns 0 on success, negative on failure.
1254 //
1255 //==========================================================================
1256
compress_bzip2(Byte * out,unsigned int * outlen,const Byte * in,unsigned int inlen)1257 int compress_bzip2(Byte *out, unsigned int *outlen, const Byte *in, unsigned int inlen)
1258 {
1259 if (BZ_OK == BZ2_bzBuffToBuffCompress((char *)out, outlen, (char *)in, inlen, 9, 0, 0))
1260 {
1261 return 0;
1262 }
1263 return -1;
1264 }
1265
1266 #ifdef PPMD
1267 //==========================================================================
1268 //
1269 // compress_ppmd
1270 //
1271 // Returns 0 on success, negative on failure.
1272 //
1273 // Big problem here: The zip format only allows for PPMd I rev. 1. This
1274 // version of the code is incompatible with 64-bit processors. PPMd J rev. 1
1275 // corrects this and also compresses slightly better, but it also changes
1276 // the data format and is incompatible with I rev. 1. The PPMd source code
1277 // is a tangled mass that I cannot comprehend, so fixing I rev. 1 to work
1278 // on 64-bit processors is well beyond my means. Hence, I cannot currently
1279 // support PPMd in zips. If the zip spec gets updated to allow J rev. 1,
1280 // then I can, but not any sooner.
1281 //
1282 //==========================================================================
1283
compress_ppmd(Byte * out,unsigned int * outlen,const Byte * in,unsigned int inlen)1284 int compress_ppmd(Byte *out, unsigned int *outlen, const Byte *in, unsigned int inlen)
1285 {
1286 int maxorder = 8;
1287 int sasize = 8;
1288 int cutoff = 0;
1289
1290 _PPMD_FILE ppsin = { (char *)in, inlen, 0 };
1291 _PPMD_FILE ppsout = { out + 2, *outlen - 2, 0};
1292
1293 if (!PPMd_StartSubAllocator(sasize))
1294 {
1295 return -1;
1296 }
1297 PPMd_EncodeFile(&ppsout, &ppsin, maxorder, cutoff);
1298 PPMd_StopSubAllocator();
1299 if (ppsout.eof)
1300 {
1301 return -1;
1302 }
1303 if (!ppsin.eof)
1304 {
1305 return -1;
1306 }
1307
1308 *(short *)out = LittleShort((maxorder - 1) + ((sasize - 1) << 4) + (cutoff << 12));
1309 *outlen = *outlen - ppsout.buffersize;
1310 return 0;
1311 }
1312 #endif
1313
1314 //==========================================================================
1315 //
1316 // compress_deflate
1317 //
1318 // Returns 0 on success, negative on failure.
1319 //
1320 //==========================================================================
1321
compress_deflate(Byte * out,unsigned int * outlen,const Byte * in,unsigned int inlen)1322 int compress_deflate(Byte *out, unsigned int *outlen, const Byte *in, unsigned int inlen)
1323 {
1324 z_stream stream;
1325 int err;
1326
1327 stream.next_in = (Bytef *)in;
1328 stream.avail_in = inlen;
1329 stream.next_out = out;
1330 stream.avail_out = *outlen;
1331 stream.zalloc = (alloc_func)0;
1332 stream.zfree = (free_func)0;
1333 stream.opaque = (voidpf)0;
1334
1335 err = deflateInit2(&stream, 9, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
1336 if (err != Z_OK) return -1;
1337
1338 err = deflate(&stream, Z_FINISH);
1339 if (err != Z_STREAM_END) {
1340 deflateEnd(&stream);
1341 return -1;
1342 }
1343 *outlen = stream.total_out;
1344
1345 err = deflateEnd(&stream);
1346 return err == Z_OK ? 0 : -1;
1347 }
1348
1349 //==========================================================================
1350 //
1351 // find_central_dir
1352 //
1353 // Finds and loads the central directory records in the file.
1354 // Taken from Quake3 source and modified.
1355 //
1356 //==========================================================================
1357
find_central_dir(FILE * fin)1358 BYTE *find_central_dir(FILE *fin)
1359 {
1360 unsigned char buf[BUFREADCOMMENT + 4];
1361 EndOfCentralDirectory eod;
1362 BYTE *dir;
1363 long file_size;
1364 long back_read;
1365 long max_back; // maximum size of global comment
1366 long pos_found = 0;
1367
1368 fseek(fin, 0, SEEK_END);
1369
1370 file_size = ftell(fin);
1371 max_back = 0xffff > file_size ? file_size : 0xffff;
1372
1373 back_read = 4;
1374 while (back_read < max_back)
1375 {
1376 UINT32 read_size, read_pos;
1377 int i;
1378 if (back_read + BUFREADCOMMENT > max_back)
1379 back_read = max_back;
1380 else
1381 back_read += BUFREADCOMMENT;
1382 read_pos = file_size - back_read;
1383
1384 read_size = (BUFREADCOMMENT + 4) < (file_size - read_pos) ?
1385 (BUFREADCOMMENT + 4) : (file_size - read_pos);
1386
1387 if (fseek(fin, read_pos, SEEK_SET) != 0)
1388 return NULL;
1389
1390 if (fread(buf, 1, read_size, fin) != read_size)
1391 return NULL;
1392
1393 for (i = (int)read_size - 3; (i--) > 0;)
1394 {
1395 if (buf[i] == 'P' && buf[i+1] == 'K' && buf[i+2] == 5 && buf[i+3] == 6)
1396 {
1397 pos_found = read_pos + i;
1398 break;
1399 }
1400 }
1401
1402 if (pos_found != 0)
1403 break;
1404 }
1405 if (pos_found == 0 ||
1406 fseek(fin, pos_found, SEEK_SET) != 0 ||
1407 fread(&eod, sizeof(eod), 1, fin) != 1 ||
1408 fseek(fin, LittleShort(eod.DirectoryOffset), SEEK_SET) != 0)
1409 {
1410 return NULL;
1411 }
1412 dir = malloc(LittleLong(eod.DirectorySize) + 4);
1413 if (dir == NULL)
1414 {
1415 no_mem = 1;
1416 return NULL;
1417 }
1418 if (fread(dir, 1, LittleLong(eod.DirectorySize), fin) != LittleLong(eod.DirectorySize))
1419 {
1420 free(dir);
1421 return NULL;
1422 }
1423 if (*(UINT32 *)dir != ZIP_CENTRALFILE)
1424 {
1425 free(dir);
1426 return NULL;
1427 }
1428 *(UINT32 *)(dir + LittleLong(eod.DirectorySize)) = ZIP_ENDOFDIR;
1429 return dir;
1430 }
1431
1432 //==========================================================================
1433 //
1434 // find_file_in_zip
1435 //
1436 // Returns a pointer to a central directory entry to a file, if it was
1437 // found in the zip's directory. Data endianness is in zip order.
1438 //
1439 //==========================================================================
1440
find_file_in_zip(BYTE * dir,const char * path,unsigned int len,unsigned int crc,short date,short time)1441 CentralDirectoryEntry *find_file_in_zip(BYTE *dir, const char *path, unsigned int len, unsigned int crc, short date, short time)
1442 {
1443 int pathlen = (int)strlen(path);
1444 CentralDirectoryEntry *ent;
1445 int flags;
1446
1447 while (*(UINT32 *)dir == ZIP_CENTRALFILE)
1448 {
1449 ent = (CentralDirectoryEntry *)dir;
1450 if (pathlen == LittleShort(ent->NameLength) &&
1451 strncmp((char *)(ent + 1), path, pathlen) == 0)
1452 {
1453 // Found something that matches by name.
1454 break;
1455 }
1456 dir += sizeof(*ent) + LittleShort(ent->NameLength) + LittleShort(ent->ExtraLength) + LittleShort(ent->CommentLength);
1457 }
1458 if (*(UINT32 *)dir != ZIP_CENTRALFILE)
1459 {
1460 return NULL;
1461 }
1462 if (crc != LittleLong(ent->CRC32))
1463 {
1464 return NULL;
1465 }
1466 if (len != LittleLong(ent->UncompressedSize))
1467 {
1468 return NULL;
1469 }
1470 // Should I check modification date and time here?
1471 flags = LittleShort(ent->Flags);
1472 if (flags & 1)
1473 { // Don't want to deal with encryption.
1474 return NULL;
1475 }
1476 if (ent->ExtraLength != 0)
1477 { // Don't want to deal with extra data.
1478 return NULL;
1479 }
1480 // Okay, looks good.
1481 return ent;
1482 }
1483
1484 //==========================================================================
1485 //
1486 // copy_zip_file
1487 //
1488 // Copies one file from ozip to zip. Returns positive on success, zero if
1489 // the file could not be found, and negative if it failed while writing the
1490 // file.
1491 //
1492 //==========================================================================
1493
copy_zip_file(FILE * zip,file_entry_t * file,FILE * ozip,CentralDirectoryEntry * ent)1494 int copy_zip_file(FILE *zip, file_entry_t *file, FILE *ozip, CentralDirectoryEntry *ent)
1495 {
1496 LocalFileHeader lfh;
1497 BYTE *buf;
1498 UINT32 buf_size;
1499
1500 if (fseek(ozip, LittleLong(ent->LocalHeaderOffset), SEEK_SET) != 0)
1501 {
1502 return 0;
1503 }
1504 if (fread(&lfh, sizeof(lfh), 1, ozip) != 1)
1505 {
1506 return 0;
1507 }
1508 // Check to make sure the local header matches the central directory.
1509 if (lfh.Flags != ent->Flags || lfh.Method != ent->Method ||
1510 lfh.CRC32 != ent->CRC32 || lfh.CompressedSize != ent->CompressedSize ||
1511 lfh.UncompressedSize != ent->UncompressedSize ||
1512 lfh.NameLength != ent->NameLength || lfh.ExtraLength != ent->ExtraLength)
1513 {
1514 return 0;
1515 }
1516 buf_size = LittleShort(lfh.NameLength) + LittleLong(lfh.CompressedSize);
1517 buf = malloc(buf_size);
1518 if (buf == NULL)
1519 {
1520 return 0;
1521 }
1522 if (fread(buf, 1, buf_size, ozip) != buf_size)
1523 {
1524 free(buf);
1525 return 0;
1526 }
1527 // Check to be sure name matches.
1528 if (strncmp((char *)buf, (char *)(ent + 1), LittleShort(lfh.NameLength)) != 0)
1529 {
1530 free(buf);
1531 return 0;
1532 }
1533 // Looks good. Let's write it in.
1534 file->zip_offset = ftell(zip);
1535 if (fwrite(&lfh, sizeof(lfh), 1, zip) != 1 ||
1536 fwrite(buf, 1, buf_size, zip) != buf_size)
1537 {
1538 free(buf);
1539 return -1;
1540 }
1541 free(buf);
1542 file->date = lfh.ModDate;
1543 file->time = lfh.ModTime;
1544 file->uncompressed_size = LittleLong(lfh.UncompressedSize);
1545 file->compressed_size = LittleLong(lfh.CompressedSize);
1546 file->method = LittleShort(lfh.Method);
1547 file->crc32 = lfh.CRC32;
1548 return 1;
1549 }
1550
1551 //==========================================================================
1552 //
1553 // main
1554 //
1555 //==========================================================================
1556
main(int argc,char ** argv)1557 int __cdecl main (int argc, char **argv)
1558 {
1559 dir_tree_t *tree, *trees;
1560 file_entry_t *file;
1561 struct stat zipstat;
1562 int needwrite;
1563 int i, j, k;
1564 int force = 0;
1565 int update = 0;
1566
1567 // Find options. Options are removed from the array.
1568 for (i = k = 1; i < argc; ++i)
1569 {
1570 if (argv[i][0] == '-')
1571 {
1572 if (argv[i][1] == '-')
1573 {
1574 if (argv[i][2] == '\0')
1575 { // -- terminates option handling for the rest of the command line
1576 break;
1577 }
1578 }
1579 for (j = 1; argv[i][j] != '\0'; ++j)
1580 {
1581 if (argv[i][j] == 'f')
1582 {
1583 force = 1;
1584 }
1585 else if (argv[i][j] == 'd')
1586 {
1587 DeflateOnly = 1;
1588 }
1589 else if (argv[i][j] == 'u')
1590 {
1591 update = 1;
1592 }
1593 else if (argv[i][j] == 'q')
1594 {
1595 Quiet = 1;
1596 }
1597 else
1598 {
1599 fprintf(stderr, "Unknown option '%c'\n", argv[i][j]);
1600 print_usage(argv[0]);
1601 return 1;
1602 }
1603 }
1604 }
1605 else
1606 {
1607 argv[k++] = argv[i];
1608 }
1609 }
1610 for (; i <= argc; ++i)
1611 {
1612 argv[k++] = argv[i];
1613 }
1614 argc -= i - k;
1615
1616 if (argc < 3)
1617 {
1618 print_usage(argv[0]);
1619 return 1;
1620 }
1621
1622 trees = add_dirs(&argv[2]);
1623 if (no_mem)
1624 {
1625 free_dir_trees(trees);
1626 fprintf(stderr, "Out of memory.\n");
1627 return 1;
1628 }
1629
1630 needwrite = force;
1631 if (stat(argv[1], &zipstat) != 0)
1632 {
1633 if (errno == ENOENT)
1634 {
1635 needwrite = 1;
1636 update = 0; // Can't update what's not there.
1637 }
1638 else
1639 {
1640 fprintf(stderr, "Could not stat %s: %s\n", argv[1], strerror(errno));
1641 }
1642 }
1643 else if (!needwrite)
1644 {
1645 // Check the files in each tree. If any one of them was modified more
1646 // recently than the zip, then it needs to be recreated.
1647 for (tree = trees; tree != NULL; tree = tree->next)
1648 {
1649 for (file = tree->files; file != NULL; file = file->next)
1650 {
1651 if (file->time_write > zipstat.st_mtime)
1652 {
1653 needwrite = 1;
1654 break;
1655 }
1656 }
1657 }
1658 }
1659 if (force || needwrite)
1660 {
1661 write_zip(argv[1], trees, update);
1662 }
1663 free_dir_trees(trees);
1664 if (no_mem)
1665 {
1666 fprintf(stderr, "Out of memory.\n");
1667 return 1;
1668 }
1669 return 0;
1670 }
1671
1672 //==========================================================================
1673 //
1674 // bz_internal_error
1675 //
1676 // libbzip2 wants this, since we build it with BZ_NO_STDIO set.
1677 //
1678 //==========================================================================
1679
bz_internal_error(int errcode)1680 void bz_internal_error (int errcode)
1681 {
1682 fprintf(stderr, "libbzip2: internal error number %d\n", errcode);
1683 exit(3);
1684 }
1685