/* 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" /* Macros for converting integers in little-endian to machine format */ #define SH(a) ((guint16)(((guint16)(guchar)(a)[0])|(((guint16)(guchar)(a)[1])<< 8))) #define LG(a) ((guint32)SH(a) | ((guint32)SH((a)+2) << 16)) /* Macros for writing machine integers to little-endian format */ #define PUTSH(a,f) {putc((char)((a) & 0xff),(f)); putc((char)((a) >> 8),(f));} #define PUTLG(a,f) {PUTSH((a) & 0xffff,(f)) PUTSH((a) >> 16,(f))} /* -- Structure of a ZIP file -- */ /* Signatures for zip file information headers */ #define LOCSIG 0x04034b50L #define CENSIG 0x02014b50L #define ENDSIG 0x06054b50L #define EXTLOCSIG 0x08074b50L /* Offsets of values in headers */ #define LOCVER 0 /* version needed to extract */ #define LOCFLG 2 /* encrypt, deflate flags */ #define LOCHOW 4 /* compression method */ #define LOCTIM 6 /* last modified file time, DOS format */ #define LOCDAT 8 /* last modified file date, DOS format */ #define LOCCRC 10 /* uncompressed crc-32 for file */ #define LOCSIZ 14 /* compressed size in zip file */ #define LOCLEN 18 /* uncompressed size */ #define LOCNAM 22 /* length of filename */ #define LOCEXT 24 /* length of extra field */ #define EXTCRC 0 /* uncompressed crc-32 for file */ #define EXTSIZ 4 /* compressed size in zip file */ #define EXTLEN 8 /* uncompressed size */ #define CENVEM 0 /* version made by */ #define CENVER 2 /* version needed to extract */ #define CENFLG 4 /* encrypt, deflate flags */ #define CENHOW 6 /* compression method */ #define CENTIM 8 /* last modified file time, DOS format */ #define CENDAT 10 /* last modified file date, DOS format */ #define CENCRC 12 /* uncompressed crc-32 for file */ #define CENSIZ 16 /* compressed size in zip file */ #define CENLEN 20 /* uncompressed size */ #define CENNAM 24 /* length of filename */ #define CENEXT 26 /* length of extra field */ #define CENCOM 28 /* file comment length */ #define CENDSK 30 /* disk number start */ #define CENATT 32 /* internal file attributes */ #define CENATX 34 /* external file attributes */ #define CENOFF 38 /* relative offset of local header */ #define ENDDSK 0 /* number of this disk */ #define ENDBEG 2 /* number of the starting disk */ #define ENDSUB 4 /* entries on this disk */ #define ENDTOT 6 /* total number of entries */ #define ENDSIZ 8 /* size of entire central directory */ #define ENDOFF 12 /* offset of central on starting disk */ #define ENDCOM 16 /* length of zip file comment */ static int ef_scan_ut_time (char *ef_buf, size_t ef_len, int ef_is_cent, iztimes *z_utim); static zlist **make_dirlist (int *nd, int *err); /* Compares the internal names z->iname */ static int zqcmp (const void *a, const void *b) { return fnamecmp((*(zlist **) a)->iname, (*(zlist **) b)->iname); } /* Compare the internal names z->iname in reverse order */ static int rqcmp (const void *a, const void *b) { return fnamecmp((*(zlist **) b)->iname, (*(zlist **) a)->iname); } #if 0 /* unused */ static guint16 makeword (const unsigned char *b) { return (guint16) ((b[1] << 8) | b[0]); } static int read_extra_attributes (zlist *z) { guint16 uid, gid; if (z->extlen < 21) { return 0; } if (z->extra[0] != 'U' || z->extra[1] != 'T') { return 0; } if (z->extra[13] != 'U' || z->extra[14] != 'x') { return 0; } uid = makeword((const unsigned char *) (z->extra + 17)); gid = makeword((const unsigned char *) (z->extra + 19)); fprintf(stderr, "uid = %d, gid = %d\n", (int) uid, (int) gid); return 0; } #endif /* 0 */ static int file_is_wanted (const char *zname, const char **fnames, char *matches) { int i; if (fnames == NULL) { return 1; } for (i=0; fnames[i] != NULL; i++) { if (!wanted_namecmp(fnames[i], zname)) { if (matches != NULL) { matches[i] = 1; } return 1; } } return 0; } /* real_read_zipfile() starts searching for the End Signature at the end of the file. The End Signature points to the Central Directory Signature which points to the Local Directory Signature. */ static int real_read_zipfile (zfile *zf, int task) { char b[CENHEAD]; /* buffer for central headers */ char buf[4096 + 4]; /* temp buffer */ guint16 flg; /* general purpose bit flag */ size_t n; /* length of name */ zlist **x; /* pointer last entry's link */ zlist *z; /* current zip entry structure */ char *t; /* temporary pointer */ char *u; /* temporary variable */ int found; guint32 cenbeg; /* start of central dir */ int err = ZE_OK; found = 0; t = &buf[4096]; t[1] = '\0'; t[2] = '\0'; t[3] = '\0'; if (fseek(zf->fp, -4096L, SEEK_END) == 0) { zf->zstart = (guint32) (ftell(zf->fp) + 4096L); while (!found && zf->zstart >= 4096) { zf->zstart -= 4096L; buf[4096] = t[1]; buf[4097] = t[2]; buf[4098] = t[3]; fread(buf, 1, 4096, zf->fp); fseek(zf->fp, -8192L, SEEK_CUR); t = &buf[4095]; while (t >= buf) { /* Check for ENDSIG the End Of Central Directory Record signature ("PK\5\6" in ASCII) */ if (LG(t) == ENDSIG) { found = 1; zf->zstart += (guint32) (t - buf); fseek(zf->fp, (long) zf->zstart + 4L, SEEK_SET); break; } --t; } } } else { zf->zstart = 4096L; } trace(3, "real_read_zipfile: zf->zstart now = %d, found = %d\n", (int) zf->zstart, found); if (!found && zf->zstart > 0) { int s; fseek(zf->fp, 0L, SEEK_SET); clearerr(zf->fp); s = fread(buf, 1, (size_t) zf->zstart, zf->fp); buf[s] = t[1]; buf[s + 1] = t[2]; buf[s + 2] = t[3]; t = &buf[s - 1]; while (t >= buf) { if (LG(t) == ENDSIG) { found = 1; zf->zstart = (guint32) (t - buf); fseek(zf->fp, (long) zf->zstart + 4L, SEEK_SET); break; } --t; } } if (!found) { trace(2, "End Of Central Directory Record not found\n"); return ZE_FORM; } /* Read the End Of Central Directory Record */ if (fread(b, ENDHEAD, 1, zf->fp) != 1) { return ferror(zf->fp) ? ZE_READ : ZE_EOF; } if (SH(ENDDSK + b) || SH(ENDBEG + b) || SH(ENDSUB + b) != SH(ENDTOT + b)) { zf->zcomlen = SH(ENDCOM + b); } if (zf->zcomlen > 0) { if ((zf->zcomment = malloc(zf->zcomlen)) == NULL) return ZE_MEM; if (fread(zf->zcomment, zf->zcomlen, 1, zf->fp) != 1) { free(zf->zcomment); zf->zcomment = NULL; return ferror(zf->fp) ? ZE_READ : ZE_EOF; } } cenbeg = zf->zstart - LG(ENDSIZ + b); if (fseek(zf->fp, cenbeg, SEEK_SET) != 0) { perror("fseek"); return ZE_FORM; } x = &zfiles; /* first link */ if (fread(b, 4, 1, zf->fp) != 1) { return ferror(zf->fp) ? ZE_READ : ZE_EOF; } while (LG(b) == CENSIG) { /* Read central header. The portion of the central header that should be in common with local header is read raw, for later comparison. (this requires that the offset of ext in the zlist structure be greater than or equal to LOCHEAD) */ if (fread(b, CENHEAD, 1, zf->fp) != 1) { return ferror(zf->fp) ? ZE_READ : ZE_EOF; } if ((z = malloc(sizeof *z)) == NULL) { return ZE_MEM; } z->version_made = SH(CENVEM + b); u = (char *) &z->version_extract; for (n = 0; n < (CENNAM-CENVER); n++) { u[n] = b[CENVER + n]; } z->namelen = SH(CENNAM + b); /* used before comparing cen vs. loc */ z->cextlen = SH(CENEXT + b); /* may be different from z->ext */ z->comlen = SH(CENCOM + b); z->dsk = SH(CENDSK + b); z->att = SH(CENATT + b); z->atx = LG(CENATX + b); z->off = LG(CENOFF + b); z->dosflag = (z->version_made & 0xff00) == 0; z->zname = z->name = z->iname = NULL; z->extra = z->cextra = NULL; z->comment = NULL; /* Link into list */ *x = z; z->nxt = NULL; x = &z->nxt; /* Read file name, extra field and comment field */ if (z->namelen == 0) { g_critical("got zero length for name of zipped file"); free(z); return ZE_FORM; } if ((z->iname = malloc(z->namelen + 1)) == NULL || (z->cextlen && (z->cextra = malloc(z->cextlen)) == NULL) || (z->comlen && (z->comment = malloc(z->comlen)) == NULL)) { return ZE_MEM; } if (fread(z->iname, z->namelen, 1, zf->fp) != 1 || (z->cextlen && fread(z->cextra, z->cextlen, 1, zf->fp) != 1) || (z->comlen && fread(z->comment, z->comlen, 1, zf->fp) != 1)) { return ferror(zf->fp) ? ZE_READ : ZE_EOF; } z->iname[z->namelen] = '\0'; /* terminate name */ /* Update zf->zstart offset, prepare for next header */ if (z->off < zf->zstart) { zf->zstart = z->off; trace(2, "z->off = %d, resetting zstart to %d\n", (int) z->off, (int) zf->zstart); } zf->zcount += 1; /* Read next signature */ if (fread(b, 4, 1, zf->fp) != 1) { return ferror(zf->fp) ? ZE_READ : ZE_EOF; } } /* Point to start of header list and read local headers */ z = zfiles; while (z != NULL && err == Z_OK) { trace(2, "reading signature at offset %d\n", (int) z->off); /* Read next signature */ if (fseek(zf->fp, z->off, SEEK_SET) != 0 || fread(b, 4, 1, zf->fp) != 1) { return ferror(zf->fp) ? ZE_READ : ZE_EOF; } if (LG(b) == LOCSIG) { if (fread(b, LOCHEAD, 1, zf->fp) != 1) { return ferror(zf->fp) ? ZE_READ : ZE_EOF; } z->lflags = SH(LOCFLG + b); n = SH(LOCNAM + b); z->extlen = SH(LOCEXT + b); /* Compare name and extra fields */ if (n != z->namelen) { trace(2, "n = %d != z->namelen = %d\n", n, z->namelen); return ZE_FORM; } if ((t = malloc(z->namelen + 1)) == NULL) { return ZE_MEM; } if (fread(t, z->namelen, 1, zf->fp) != 1) { free(t); return ferror(zf->fp) ? ZE_READ : ZE_EOF; } if (memcmp(t, z->iname, z->namelen)) { t[z->namelen] = 0; trace(2, "t = '%s' != z->iname = '%s'\n", n, z->namelen); free(t); return ZE_FORM; } free(t); if (z->extlen) { if ((z->extra = malloc(z->extlen)) == NULL) { return ZE_MEM; } if (fread(z->extra, z->extlen, 1, zf->fp) != 1) { free(z->extra); return ferror(zf->fp) ? ZE_READ : ZE_EOF; } if (z->extlen == z->cextlen && memcmp(z->extra, z->cextra, z->extlen) == 0) { free(z->extra); z->extra = z->cextra; } } /* Check extended local header if there is one */ if (z->lflags & 8) { char buf2[16]; guint32 s; /* size of compressed data */ s = LG(LOCSIZ + b); if (s == 0) { s = LG((CENSIZ-CENVER) + (char *) &z->version_extract); } if (fseek(zf->fp, (z->off + (4+LOCHEAD) + z->namelen + z->extlen + s), SEEK_SET) || (fread(buf2, 16, 1, zf->fp) != 1)) { return ferror(zf->fp) ? ZE_READ : ZE_EOF; } if (LG(buf2) != EXTLOCSIG) { trace(2, "expected extended local signature\n"); return ZE_FORM; } trace(2, "extended header: s (compressed size) = %d\n", (int) s); /* overwrite the unknown values of the local header */ for (n = 0; n < 12; n++) { b[LOCCRC+n] = buf2[4+n]; } } /* Compare local header with that part of central header (except for the reserved bits in the general purpose flags and except for the already checked entry name length) */ u = (char *) &z->version_extract; flg = SH((CENFLG-CENVER) + u); /* Save central flags word */ u[CENFLG-CENVER+1] &= 0x1f; /* Mask reserved flag bits */ b[LOCFLG+1] &= 0x1f; for (n = 0; n < LOCNAM; n++) { if (b[n] != u[n]) { /* local and central headers disagree */ trace(2, "local and central headers disagree\n"); return ZE_FORM; } } /* Complete the setup of the zlist entry by translating the remaining central header fields in memory, starting with the fields with highest offset. This order of the conversion commands takes into account potential buffer overlaps caused by structure padding. */ z->usize = LG((CENLEN-CENVER) + u); z->csize = LG((CENSIZ-CENVER) + u); z->crc = LG((CENCRC-CENVER) + u); z->time = LG((CENTIM-CENVER) + u); /* time and date into one long */ z->method = SH((CENHOW-CENVER) + u); z->flags = flg; /* may be different from z->lflags */ z->version_extract = SH((CENVER-CENVER) + u); trace(2, "'%s': usize=%d, csize=%d\n", z->iname, (int) z->usize, (int) z->csize); trace(2, " crc=%lu, time=%d, method=%d\n", z->crc, (int) z->time, (int) z->method); /* Clear actions */ z->mark = MARK_NONE; z->zname = internal_to_external(z->iname); /* convert to external name */ if (z->zname == NULL) { return ZE_MEM; } z->name = z->zname; if (task == ZIP_DO_UNZIP) { if (file_is_wanted(z->zname, zf->wanted, zf->matches)) { long datapos = z->off + (4 + LOCHEAD) + z->namelen + z->extlen; err = decompress_to_file(zf, z, datapos); if (!err) { z->mark = MARK_UNZIP; } } } else if (task == ZIP_DO_DELETE) { if (file_is_wanted(z->zname, zf->wanted, zf->matches)) { trace(1, "'%s': scheduling for deletion\n", z->zname); z->mark = MARK_DELETE; } } } else { trace(2, "expected to find local signature at %d\n", (int) z->off); return ZE_FORM; } z = z->nxt; } if (zf->strm_initted) { inflateEnd(&zf->strm); zf->strm_initted = 0; } trace(3, "real_read_zipfile: at return zf->zstart = %d\n", (int) zf->zstart); return err; } int read_zipfile (zfile *zf, int task) { int err = 0; if (zf->fname == NULL || *zf->fname == '\0') { return 0; } zf->fp = gretl_fopen(zf->fname, "rb"); if (zf->fp == NULL) { if (task == ZIP_DO_ZIP) { return 0; } else { return ZE_OPEN; } } err = real_read_zipfile(zf, task); fclose(zf->fp); zf->fp = NULL; /* If we have one or more files, sort by name */ if (!err && zf->zcount && task == ZIP_DO_ZIP) { zlist **x; /* pointer into zsort array */ zlist *z; /* pointer into zfiles linked list */ x = zf->zsort = malloc(zf->zcount * sizeof z); if (x == NULL) { return ZE_MEM; } for (z = zfiles; z != NULL; z = z->nxt) { *x++ = z; } qsort(zf->zsort, zf->zcount, sizeof z, zqcmp); } if (!err && zf->zcount && task == ZIP_DO_UNZIP) { /* unzipping: fix up directory permissions */ unsigned attr; zlist **s; int i, nd = 0; s = make_dirlist(&nd, &err); if (s != NULL) { for (i=0; iname; if (*p == '\0') continue; if (p[strlen(p) - 1] == '/') { p[strlen(p) - 1] = '\0'; } if (i == 0 || strcmp(s[i]->name, s[i-1]->name) != 0) { attr = (unsigned) (s[i]->atx >> 16); if (attr) { chmod(s[i]->name, attr); } } } free(s); } } return err; } /* Write a local header described by *z to file *f. Return an error code in the ZE_ class. */ int put_local_header (zlist *z, FILE *fp) { PUTLG(LOCSIG, fp); PUTSH(z->version_extract, fp); PUTSH(z->lflags, fp); PUTSH(z->method, fp); PUTLG(z->time, fp); PUTLG(z->crc, fp); PUTLG(z->csize, fp); PUTLG(z->usize, fp); PUTSH(z->namelen, fp); PUTSH(z->extlen, fp); if (fwrite(z->iname, 1, z->namelen, fp) != z->namelen || (z->extlen && fwrite(z->extra, 1, z->extlen, fp) != z->extlen)) { return ZE_TEMP; } return ZE_OK; } /* Write an extended local header described by *z to file */ int put_extended_header (zlist *z, FILE *fp) { PUTLG(EXTLOCSIG, fp); PUTLG(z->crc, fp); PUTLG(z->csize, fp); PUTLG(z->usize, fp); return ZE_OK; } /* Write a central header described by *z to file. Return an error code in the ZE_ class. */ int put_central_header (zlist *z, FILE *fp) { PUTLG(CENSIG, fp); PUTSH(z->version_made, fp); PUTSH(z->version_extract, fp); PUTSH(z->flags, fp); PUTSH(z->method, fp); PUTLG(z->time, fp); PUTLG(z->crc, fp); PUTLG(z->csize, fp); PUTLG(z->usize, fp); PUTSH(z->namelen, fp); PUTSH(z->cextlen, fp); PUTSH(z->comlen, fp); PUTSH(z->dsk, fp); PUTSH(z->att, fp); PUTLG(z->atx, fp); PUTLG(z->off, fp); if (fwrite(z->iname, 1, z->namelen, fp) != z->namelen || (z->cextlen && fwrite(z->cextra, 1, z->cextlen, fp) != z->cextlen) || (z->comlen && fwrite(z->comment, 1, z->comlen, fp) != z->comlen)) { return ZE_TEMP; } return ZE_OK; } /* Write the end of central directory data to file. Return an error code in the ZE_ class. */ int put_end_dir (int nentries, guint32 dirsize, guint32 offset, size_t zcomlen, const char *comment, FILE *fp) { PUTLG(ENDSIG, fp); PUTSH(0, fp); PUTSH(0, fp); PUTSH(nentries, fp); PUTSH(nentries, fp); PUTLG(dirsize, fp); PUTLG(offset, fp); PUTSH(zcomlen, fp); /* Write the comment, if any */ if (zcomlen > 0 && fwrite(comment, 1, zcomlen, fp) != zcomlen) { return ZE_TEMP; } return ZE_OK; } /* Copy the zip entry described by *z from file *fp to file *fq. Return an error code in the ZE_ class. Update tempzn by the number of bytes copied. */ int zipcopy (zfile *zf, zlist *z, FILE *fp, FILE *fq) { /* local header offset */ guint32 n = (4 + LOCHEAD) + z->namelen + z->extlen; if (fseek(fp, z->off, SEEK_SET)) { return ferror(fp)? ZE_READ : ZE_EOF; } z->off = zf->tempzn; n += z->csize; trace(2, "z->csize = %d\n", (int) z->csize); /* copy the compressed data and the extended local header, if any */ if (z->lflags & 8) { n += 16; } zf->tempzn += n; trace(2, "zipcopy: added %d to tempzn, which now = %d\n", (int) n, (int) zf->tempzn); return fcopy(fp, fq, n); } /* This function scans the extra field for EF_TIME or EF_IZUNIX blocks containing Unix style time_t (GMT) values for the entry's access, creation and modification time. If a valid block is found, all time stamps are copied to the iztimes structure. The presence of an EF_TIME or EF_IZUNIX2 block results in ignoring all data from probably present obsolete EF_IZUNIX blocks. If multiple blocks of the same type are found, only the information from the last block is used. The return value is the EF_TIME Flags field (simulated in case of an EF_IZUNIX block) or 0 in case of failure. ef_buf: buffer containing extra field ef_len: total length of extra field central: flag indicating "is central extra field" z_utim: return storage: atime, mtime, ctime */ static int ef_scan_ut_time (char *ef_buf, size_t ef_len, int central, iztimes *z_utim) { int flags = 0; unsigned eb_id; size_t eb_len; int have_new_type_eb = 0; if (ef_len == 0 || ef_buf == NULL) { return 0; } trace(2, "ef_scan_ut_time: scanning extra field of length %d\n", (int) ef_len); while (ef_len >= EB_HEADSIZE) { eb_id = SH(EB_ID + ef_buf); eb_len = SH(EB_LEN + ef_buf); if (eb_len > (ef_len - EB_HEADSIZE)) { /* Discovered some extra field inconsistency! */ trace(2, "ef_scan_ut_time: block length %u > rest ef_size %u\n", eb_len, ef_len - EB_HEADSIZE); break; } switch (eb_id) { case EF_TIME: flags &= ~0x00ff; /* ignore previous IZUNIX or EF_TIME fields */ have_new_type_eb = 1; if (eb_len >= EB_UT_MINLEN && z_utim != NULL) { unsigned eb_idx = EB_UT_TIME1; trace(2, "ef_scan_ut_time: Found TIME extra field\n"); flags |= (ef_buf[EB_HEADSIZE+EB_UT_FLAGS] & 0x00ff); if ((flags & EB_UT_FL_MTIME)) { if ((eb_idx+4) <= eb_len) { z_utim->mtime = LG((EB_HEADSIZE+eb_idx) + ef_buf); eb_idx += 4; trace(2, " Unix EF mtime = %ld\n", z_utim->mtime); } else { flags &= ~EB_UT_FL_MTIME; trace(2, " Unix EF truncated, no mtime\n"); } } if (central) { break; /* central version of TIME field ends here */ } if (flags & EB_UT_FL_ATIME) { if ((eb_idx+4) <= eb_len) { z_utim->atime = LG((EB_HEADSIZE+eb_idx) + ef_buf); eb_idx += 4; trace(2, " Unix EF atime = %ld\n", z_utim->atime); } else { flags &= ~EB_UT_FL_ATIME; } } if (flags & EB_UT_FL_CTIME) { if ((eb_idx+4) <= eb_len) { z_utim->ctime = LG((EB_HEADSIZE+eb_idx) + ef_buf); /* eb_idx += 4; */ /* superfluous for now ... */ trace(2, " Unix EF ctime = %ld\n", z_utim->ctime); } else { flags &= ~EB_UT_FL_CTIME; } } } break; case EF_IZUNIX2: if (!have_new_type_eb) { flags &= ~0x00ff; /* ignore any previous IZUNIX field */ have_new_type_eb = 1; } break; case EF_IZUNIX: if (eb_len >= EB_UX_MINLEN) { trace(2, "ef_scan_ut_time: Found IZUNIX extra field\n"); if (have_new_type_eb) { break; /* Ignore IZUNIX extra field block ! */ } z_utim->atime = LG((EB_HEADSIZE+EB_UX_ATIME) + ef_buf); z_utim->mtime = LG((EB_HEADSIZE+EB_UX_MTIME) + ef_buf); trace(2, " Unix EF access time = %ld\n", z_utim->atime); trace(2, " Unix EF modif. time = %ld\n", z_utim->mtime); flags |= (EB_UT_FL_MTIME | EB_UT_FL_ATIME); /* signal success */ } break; default: break; } /* Skip this extra field block */ ef_buf += (eb_len + EB_HEADSIZE); ef_len -= (eb_len + EB_HEADSIZE); } return flags; } #define EF_ASIUNIX 0x756e #define EB_ASI_MODE 4 static int ef_scan_mode (char *ef_buf, size_t ef_len, int central) { unsigned eb_id; size_t eb_len; int attr = 0; if (ef_len == 0 || ef_buf == NULL) { return 0; } trace(2, "ef_scan_mode: scanning extra field of length %d\n", (int) ef_len); while (ef_len >= EB_HEADSIZE) { eb_id = SH(EB_ID + ef_buf); eb_len = SH(EB_LEN + ef_buf); if (eb_len > (ef_len - EB_HEADSIZE)) { /* Discovered some extra field inconsistency! */ trace(2, "ef_scan_mode: block length %u > rest ef_size %u\n", eb_len, ef_len - EB_HEADSIZE); break; } if (eb_id == EF_ASIUNIX) { trace(2, "got EF_ASIUNIX field\n"); if (eb_len >= (EB_ASI_MODE + 2)) { attr = SH(ef_buf + (EB_HEADSIZE + EB_ASI_MODE)); break; } } ef_len -= (eb_len + EB_HEADSIZE); ef_buf += (eb_len + EB_HEADSIZE); } return attr; } int get_ef_ut_ztime (zlist *z, iztimes *z_utim) { int r; /* first scan local extra field */ r = ef_scan_ut_time(z->extra, z->extlen, 0, z_utim); /* If that didn't work, try central extra field, but only if it's really different. */ if (!r && z->cextlen > 0 && z->cextra != z->extra) { r = ef_scan_ut_time(z->cextra, z->cextlen, 1, z_utim); } return r; } int get_ef_mode (zlist *z) { int r; /* first scan local extra field */ r = ef_scan_mode(z->extra, z->extlen, 0); /* If that didn't work, try central extra field, but only if it's really different. */ if (!r && z->cextlen > 0 && z->cextra != z->extra) { r = ef_scan_mode(z->cextra, z->cextlen, 1); } return r; } #define z_is_dir(z) (z->namelen > 0 && z->iname[z->namelen - 1] == '/') zlist **make_dirlist (int *pnd, int *err) { zlist **dirs = NULL; zlist *z; int nd = *pnd; if (nd == 0) { for (z = zfiles; z != NULL; z = z->nxt) { if (z->mark && z_is_dir(z)) { nd++; } } } if (nd > 0) { dirs = malloc(nd * sizeof *dirs); if (dirs == NULL) { *err = ZE_MEM; return NULL; } nd = 0; for (z = zfiles; z != NULL; z = z->nxt) { if (z->mark && z_is_dir(z) && (nd == 0 || strcmp(z->name, dirs[nd - 1]->name))) { dirs[nd++] = z; } } /* sort in reverse order to get subdirs first */ qsort(dirs, nd, sizeof *dirs, rqcmp); } *pnd = nd; return dirs; } /* Delete the compressed files and the directories that contained the deleted files, if empty. Return an error code in the ZE_ class. Failure of remove() or rmdir() is ignored. */ int delete_input_files (void) { zlist **s; /* table of zip entries to handle, sorted */ zlist *z; /* current zip entry */ int i, nd = 0; /* delete marked files and count directories */ for (z = zfiles; z != NULL; z = z->nxt) { if (z->mark == MARK_ZIP) { if (!z_is_dir(z)) { remove(z->name); } else { nd++; } } } if (nd > 0) { int err = 0; s = make_dirlist(&nd, &err); if (err) { return err; } for (i=0; iname; if (*p == '\0') continue; if (p[strlen(p) - 1] == '/') { p[strlen(p) - 1] = '\0'; } if (i == 0 || strcmp(s[i]->name, s[i-1]->name) != 0) { rmdir(s[i]->name); } } free(s); } return ZE_OK; }