/* * Author: * Guido Draheim * * Copyright (c) 1999,2000,2001,2002,2003 Guido Draheim * All rights reserved, * use under the restrictions of the * Lesser GNU General Public License * or alternatively the restrictions * of the Mozilla Public License 1.1 */ #include /* exported... */ #include #include /*offsetof */ #include #include #include #ifdef ZZIP_HAVE_SYS_STAT_H #include #else #include #endif #include #ifndef offsetof #pragma warning had to DEFINE offsetof as it was not in stddef.h #define offsetof(T,M) ((unsigned)(& ((T*)0)->M)) #endif #ifdef ZZIP_HAVE_SYS_STAT_H /* MSVC does have IFbitmask but not the corresponding IStests */ # if ! defined S_ISDIR && defined S_IFDIR # define S_ISDIR(_X_) ((_X_) & S_IFDIR) # endif # if ! defined S_ISREG && defined S_IFREG # define S_ISREG(_X_) ((_X_) & S_IFREG) # endif #endif /** * This function is the equivalent of a => rewinddir(2) for a realdir or * the zipfile in place of a directory. The ZZIP_DIR handle returned from * => zzip_opendir has a flag saying realdir or zipfile. As for a zipfile, * the filenames will include the filesubpath, so take care. */ void zzip_rewinddir(ZZIP_DIR * dir) { if (! dir) return; if (USE_DIRENT && dir->realdir) { _zzip_rewinddir(dir->realdir); return; } if (dir->hdr0) dir->hdr = dir->hdr0; else dir->hdr = 0; } #if ! USE_DIRENT #define real_readdir(_X_) 1 #else static int real_readdir(ZZIP_DIR * dir) { struct stat st = { 0 }; char filename[PATH_MAX]; struct dirent *dirent = _zzip_readdir(dir->realdir); if (! dirent) return 0; dir->dirent.d_name = dirent->d_name; strcpy(filename, dir->realname); strcat(filename, "/"); strcat(filename, dirent->d_name); if (stat(filename, &st) == -1) return -1; dir->dirent.d_csize = dir->dirent.st_size = st.st_size; if (st.st_mode) { if (! S_ISREG(st.st_mode)) { dir->dirent.d_compr = st.st_mode; dir->dirent.d_compr |= 0x80000000; /* makes it effectively negative, * but can still be fed to S_ISXXX(x) */ } else { dir->dirent.d_compr = 0; /* stored */ } } else { dir->dirent.d_compr = 0; /* stored */ } return 1; } #endif /** * This function is the equivalent of a => readdir(2) for a realdir * or a zipfile referenced by the ZZIP_DIR returned from => zzip_opendir. * * The ZZIP_DIR handle (as returned by => zzip_opendir) contains a few more * entries than being copied into the ZZIP_DIRENT. The only valid fields in * a ZZIP_DIRENT are d_name (the file name), d_compr (compression), d_csize * (compressed size), st_size (uncompressed size). */ ZZIP_DIRENT * zzip_readdir(ZZIP_DIR * dir) { if (! dir) { errno=EBADF; return 0; } if (USE_DIRENT && dir->realdir) { if (! real_readdir(dir)) return 0; } else { if (! dir->hdr) return 0; dir->dirent.d_name = dir->hdr->d_name; dir->dirent.d_compr = dir->hdr->d_compr; dir->dirent.d_csize = dir->hdr->d_csize; dir->dirent.st_size = dir->hdr->d_usize; if (! dir->hdr->d_reclen) dir->hdr = 0; else dir->hdr = (struct zzip_dir_hdr *) ((char *) dir->hdr + dir->hdr->d_reclen); } return &dir->dirent; } /** => zzip_rewinddir * This function is the equivalent of => telldir(2) for a realdir or zipfile. */ zzip_off_t zzip_telldir(ZZIP_DIR * dir) { if (! dir) { errno=EBADF; return -1; } if (USE_DIRENT && dir->realdir) { return _zzip_telldir(dir->realdir); } else { return ((zzip_off_t) ((char *) dir->hdr - (char *) dir->hdr0)); } } /** => zzip_rewinddir * This function is the equivalent of => seekdir(2) for a realdir or zipfile. */ void zzip_seekdir(ZZIP_DIR * dir, zzip_off_t offset) { if (! dir) return; if (USE_DIRENT && dir->realdir) { _zzip_seekdir(dir->realdir, offset); } else { dir->hdr = (struct zzip_dir_hdr *) (dir->hdr0 ? (char *) dir->hdr0 + (size_t) offset : 0); } } #ifndef EOVERFLOW #define EOVERFLOW EFBIG #endif /** => zzip_rewinddir * This function is provided for users who can not use any largefile-mode. */ long zzip_telldir32(ZZIP_DIR * dir) { if (sizeof(zzip_off_t) == sizeof(long)) { return zzip_telldir(dir); } else { off_t off = zzip_telldir(dir); if (off >= 0) { register long off32 = off; if (off32 == off) return off32; errno = EOVERFLOW; } return -1; } } /** => zzip_rewinddir * This function is provided for users who can not use any largefile-mode. */ void zzip_seekdir32(ZZIP_DIR * dir, long offset) { zzip_seekdir(dir, offset); } #if defined ZZIP_LARGEFILE_RENAME && defined EOVERFLOW && defined PIC #undef zzip_seekdir /* zzip_seekdir64 */ #undef zzip_telldir /* zzip_telldir64 */ /* DLL compatibility layer - so that 32bit code can link with a 64on32 too */ long zzip_telldir(ZZIP_DIR * dir) { return zzip_telldir32(dir); } void zzip_seekdir(ZZIP_DIR * dir, long offset) { zzip_seekdir32(dir, offset); } #endif /** * This function is the equivalent of => opendir(3) for a realdir or zipfile. * * This function has some magic - if the given argument-path * is a directory, it will wrap a real => opendir(3) into the ZZIP_DIR * structure. Otherwise it will divert to => zzip_dir_open which * can also attach a ".zip" extension if needed to find the archive. * * the error-code is mapped to => errno(3). */ ZZIP_DIR * zzip_opendir(zzip_char_t * filename) { return zzip_opendir_ext_io(filename, 0, 0, 0); } /** => zzip_opendir * This function uses explicit ext and io instead of the internal * defaults, setting them to zero is equivalent to => zzip_opendir */ ZZIP_DIR * zzip_opendir_ext_io(zzip_char_t * filename, int o_modes, zzip_strings_t * ext, zzip_plugin_io_t io) { zzip_error_t e; ZZIP_DIR *dir; # ifdef ZZIP_HAVE_SYS_STAT_H struct stat st; # endif if (o_modes & (ZZIP_PREFERZIP | ZZIP_ONLYZIP)) goto try_zzip; try_real: # ifdef ZZIP_HAVE_SYS_STAT_H if (stat(filename, &st) >= 0 && S_ISDIR(st.st_mode)) { if (USE_DIRENT) { _zzip_DIR *realdir = _zzip_opendir(filename); if (realdir) { if (! (dir = (ZZIP_DIR *) calloc(1, sizeof(*dir)))) { _zzip_closedir(realdir); return 0; } else { dir->realdir = realdir; dir->realname = strdup(filename); return dir; } } } return 0; } # endif /* HAVE_SYS_STAT_H */ try_zzip: dir = zzip_dir_open_ext_io(filename, &e, ext, io); if (! dir && (o_modes & ZZIP_PREFERZIP)) goto try_real; if (e) errno = zzip_errno(e); return dir; } /** * This function is the equivalent of => closedir(3) for a realdir or zipfile. * * This function is magic - if the given arg-ZZIP_DIR * is a real directory, it will call the real => closedir(3) and then * free the wrapping ZZIP_DIR structure. Otherwise it will divert * to => zzip_dir_close which will free the ZZIP_DIR structure. */ int zzip_closedir(ZZIP_DIR * dir) { if (! dir) { errno = EBADF; return -1; } if (USE_DIRENT && dir->realdir) { _zzip_closedir(dir->realdir); free(dir->realname); free(dir); return 0; } else { zzip_dir_close(dir); return 0; } } /* * Local variables: * c-file-style: "stroustrup" * End: */