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