/*
The code here is based on code by Mark Adler et al. which is
Copyright (c) 1990-2005 Info-ZIP. Specifically, it derives from zip
version 2.31. Modifications are by Allin Cottrell, March, 2006.
Please see the included file "LICENSE" which contains the Info-ZIP
license information.
*/
#include "zunz_private.h"
#include <time.h>
#include <errno.h>
/* Compare a target to an entry in the zfile list */
static int zbcmp (const void *n, const void *z)
{
return fnamecmp((char *) n, ((zlist *) z)->zname);
}
static void *search (const void *b, const void **a, size_t n,
int (*cmp) (const void *, const void *))
{
const void **i; /* pointer to midpoint of current range */
const void **l; /* pointer to lower end of current range */
const void **u; /* pointer to upper end of current range */
int r;
l = a;
u = l + (n - 1);
while (u >= l) {
i = l + ((unsigned) (u - l) >> 1);
if ((r = (*cmp)(b, (const char *) *(zlist **) i)) < 0) {
u = i - 1;
} else if (r > 0) {
l = i + 1;
} else {
return (void *) i;
}
}
return NULL; /* If b were in list, it would belong at l */
}
/* Return a pointer to the entry in zfile with the given name , or
NULL if not found. */
static zlist *zsearch (zfile *zf, const char *name)
{
zlist *ret = NULL;
if (zf->zcount > 0) {
zlist **pz = (zlist **) search(name, (const void **) zf->zsort,
zf->zcount, zbcmp);
if (pz != NULL) {
ret = *pz;
}
}
return ret;
}
/* Delete the entry *f in the doubly-linked 'found' list. Return
pointer to next entry to allow stepping through list. */
flist *flist_expel (flist *f, int *fcount)
{
flist *t;
t = f->nxt;
*(f->lst) = t; /* point last to next, */
if (t != NULL) {
t->lst = f->lst; /* and next to last */
}
if (f->name != NULL) {
free(f->name);
}
if (f->zname != NULL) {
free(f->zname);
}
if (f->iname != NULL) {
free(f->iname);
}
free(f);
*fcount -= 1;
return t;
}
flist *flist_entry_new (const char *name, char *iname, char *zname,
zfile *zf)
{
flist *f;
/* check for overflow */
if (zf->fcount + 1 < zf->fcount) {
return NULL;
}
f = malloc(sizeof *f);
if (f == NULL) {
return NULL;
}
f->name = g_strdup(name);
f->iname = iname;
f->zname = zname;
*fnxt = f;
f->lst = fnxt;
f->nxt = NULL;
fnxt = &f->nxt;
zf->fcount += 1;
return f;
}
/* Add (or exclude) the name of an existing disk file. Return an
error code in the ZE_ class. */
int newname (const char *name, zfile *zf)
{
char *iname = NULL; /* internal version of filename */
char *zname = NULL; /* external version of filename */
flist *f = NULL; /* where in found, or new found entry */
zlist *z = NULL; /* where in zlist (if found) */
/* convert from given filename to internal zip filename */
iname = external_to_internal(name, zf);
if (iname == NULL) {
return ZE_MEM;
}
/* check for empty internal name */
if (*iname == '\0') {
free(iname);
return ZE_OK;
}
/* translate from zipfile name to canonical external name */
zname = internal_to_external(iname);
if (zname == NULL) {
return ZE_MEM;
}
/* search for name in the zip file */
z = zsearch(zf, zname);
if (z != NULL) {
/* corresponding name found in zipfile */
trace(2, " '%s': is in the zipfile, setting mark\n", zname);
z->mark = MARK_ZIP;
z->name = g_strdup(name);
z->dosflag = 0;
free(iname);
free(zname);
} else {
/* name not found in zipfile */
static struct stat zipstatb;
struct stat statb;
if (zf->state == ZF_STATE_UNKNOWN) {
if (stat(zf->fname, &zipstatb) == 0) {
/* target zipfile already exists */
zf->state = ZF_STATE_OLD;
} else {
zf->state = ZF_STATE_NEW;
}
}
if (zf->state == ZF_STATE_NEW
&& (statb = zipstatb, stat(name, &statb) == 0
&& zipstatb.st_mode == statb.st_mode
&& zipstatb.st_ino == statb.st_ino
&& zipstatb.st_dev == statb.st_dev
&& zipstatb.st_uid == statb.st_uid
&& zipstatb.st_gid == statb.st_gid
&& zipstatb.st_size == statb.st_size
&& zipstatb.st_mtime == statb.st_mtime
&& zipstatb.st_ctime == statb.st_ctime)) {
/* the given name is in fact the target zipfile: so
bypass it! */
free(zname);
free(iname);
return ZE_OK;
}
trace(2, " '%s': not in existing zipfile, adding flist entry\n",
zname);
f = flist_entry_new(name, iname, zname, zf);
if (f == NULL) {
free(iname);
free(zname);
return ZE_MEM;
}
}
return ZE_OK;
}
/* Convert the date yr/mon/day and time hr:min:sec to a four-byte DOS
date and time (date in high two bytes, time in low two bytes
allowing magnitude comparison).
*/
guint32 dostime (int yr, int mon, int day, int hr, int min, int sec)
{
return (yr < 1980)? DOSTIME_MINIMUM /* dostime(1980, 1, 1, 0, 0, 0) */ :
(((guint32) yr - 1980) << 25) | ((guint32) mon << 21) | ((guint32) day << 16) |
((guint32) hr << 11) | ((guint32) min << 5) | ((guint32) sec >> 1);
}
/* Return the Unix time t in DOS format, rounded up to the next two
second boundary. */
guint32 unix2dostime (time_t *t)
{
time_t t_even;
struct tm *s;
t_even = (time_t) (((unsigned long)(*t) + 1) & (~1));
s = localtime(&t_even); /* Use local time since MSDOS does. */
if (s == NULL) {
/* time conversion error; use current time as emergency value
(assuming that localtime() does at least accept this value!) */
t_even = (time_t) (((unsigned long) time(NULL) + 1) & (~1));
s = localtime(&t_even);
}
return dostime(s->tm_year + 1900, s->tm_mon + 1, s->tm_mday,
s->tm_hour, s->tm_min, s->tm_sec);
}
int is_symlink (guint32 attr)
{
#ifdef S_IFLNK
return ((attr >> 16) & S_IFMT) == S_IFLNK;
#else
return 0;
#endif
}
/* Return the Unix time_t value (GMT/UTC time) for the DOS format
(local) time dost, where dost is a four byte value (date in most
significant word, time in least significant word), see dostime()
function.
*/
time_t dos2unixtime (guint32 dost)
{
time_t clock = time(NULL);
struct tm *t;
t = localtime(&clock);
t->tm_isdst = -1; /* let mktime() determine if DST is in effect */
t->tm_sec = (((int) dost) << 1) & 0x3e;
t->tm_min = (((int) dost) >> 5) & 0x3f;
t->tm_hour = (((int) dost) >> 11) & 0x1f;
t->tm_mday = (int) (dost >> 16) & 0x1f;
t->tm_mon = ((int) (dost >> 21) & 0x0f) - 1;
t->tm_year = ((int) (dost >> 25) & 0x7f) + 80;
return mktime(t);
}
/* Replace file @dest by file @src, then remove @src. Return an error
code in the ZE_ class. This function need not preserve the file
attributes, this will be done by set_file_attributes() later.
*/
int replace_file (char *dest, char *src)
{
struct stat t;
int copy = 0;
int d_exists;
d_exists = (LSTAT(dest, &t) == 0);
#ifdef WIN32
if (d_exists) {
remove(dest);
}
#else
if (d_exists) {
if (t.st_nlink > 1 || (t.st_mode & S_IFMT) == S_IFLNK) {
copy = 1;
} else if (remove(dest)) {
return ZE_CREAT;
}
}
#endif
if (!copy) {
if (rename(src, dest)) {
copy = 1;
if (errno != EXDEV) {
return ZE_CREAT;
}
}
}
if (copy) {
FILE *fs, *fd;
int err;
if ((fs = gretl_fopen(src, "rb")) == NULL) {
fprintf(stderr," replace: can't open %s\n", src);
return ZE_TEMP;
}
if ((fd = gretl_fopen(dest, "wb")) == NULL) {
fclose(fs);
return ZE_CREAT;
}
err = fcopy(fs, fd, (guint32)-1L);
fclose(fs);
if (fclose(fd) || err != ZE_OK) {
remove(dest);
return err ? (err == ZE_TEMP ? ZE_WRITE : err) : ZE_WRITE;
}
remove(src);
}
return ZE_OK;
}
int get_file_attributes (const char *fname)
{
struct stat s;
return (stat(fname, &s) == 0)? (int) s.st_mode : 0;
}
#define lastchar(s) ((*(s) == '\0')? '\0' : s[strlen(s)-1])
/* make a temporary file name, using tempath */
char *ztempname (const char *tempath)
{
char *tmp = NULL;
char *ret = NULL;
if (tempath != NULL) {
tmp = malloc(strlen(tempath) + 12);
if (tmp != NULL) {
strcpy(tmp, tempath);
if (lastchar(tmp) != '/') {
strcat(tmp, "/");
}
}
} else {
tmp = malloc(12);
if (tmp != NULL) {
*tmp = 0;
}
}
if (tmp != NULL) {
strcat(tmp, "ziXXXXXX");
ret = mktemp(tmp);
}
return ret;
}
/* Copy n bytes from file *f to file *g, or until EOF if n == -1. Return
an error code in the ZE_ class. */
int fcopy (FILE *f, FILE *g, guint32 n)
{
guchar b[WSIZE];
size_t k, csiz;
guint32 copied = 0;
while (n == (guint32)(-1L) || copied < n) {
if (n == (guint32)(-1)) {
csiz = WSIZE;
} else if (n - copied < WSIZE) {
csiz = n - copied;
} else {
csiz = WSIZE;
}
if ((k = fread(b, 1, csiz, f)) == 0) {
if (ferror(f)) {
return ZE_READ;
} else {
break;
}
}
if (fwrite(b, 1, k, g) != k) {
fprintf(stderr," fcopy: write error\n");
return ZE_TEMP;
}
copied += k;
}
return ZE_OK;
}
syntax highlighted by Code2HTML, v. 0.9.1