summaryrefslogtreecommitdiffstats
path: root/generic/tclZipfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclZipfs.c')
-rw-r--r--generic/tclZipfs.c6595
1 files changed, 0 insertions, 6595 deletions
diff --git a/generic/tclZipfs.c b/generic/tclZipfs.c
deleted file mode 100644
index d902ad8..0000000
--- a/generic/tclZipfs.c
+++ /dev/null
@@ -1,6595 +0,0 @@
-/*
- * tclZipfs.c --
- *
- * Implementation of the ZIP filesystem used in TIP 430
- * Adapted from the implementation for AndroWish.
- *
- * Copyright © 2016-2017 Sean Woods <yoda@etoyoc.com>
- * Copyright © 2013-2015 Christian Werner <chw@ch-werner.de>
- *
- * See the file "license.terms" for information on usage and redistribution of
- * this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * This file is distributed in two ways:
- * generic/tclZipfs.c file in the TIP430-enabled Tcl cores.
- * compat/tclZipfs.c file in the tclconfig (TEA) file system, for pre-tip430
- * projects.
- *
- * Helpful docs:
- * https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.9.TXT
- * https://libzip.org/specifications/appnote_iz.txt
- */
-
-#include "tclInt.h"
-#include "tclFileSystem.h"
-
-#include <assert.h>
-
-#ifndef _WIN32
-#include <sys/mman.h>
-#endif /* _WIN32*/
-
-#ifndef MAP_FILE
-#define MAP_FILE 0
-#endif /* !MAP_FILE */
-#define NOBYFOUR
-#ifndef TBLS
-#define TBLS 1
-#endif
-
-#if !defined(_WIN32) && !defined(NO_DLFCN_H)
-#include <dlfcn.h>
-#endif
-
-/*
- * Macros to report errors only if an interp is present.
- */
-
-#define ZIPFS_ERROR(interp,errstr) \
- do { \
- if (interp) { \
- Tcl_SetObjResult(interp, Tcl_NewStringObj(errstr, -1)); \
- } \
- } while (0)
-#define ZIPFS_MEM_ERROR(interp) \
- do { \
- if (interp) { \
- Tcl_SetObjResult(interp, Tcl_NewStringObj( \
- "out of memory", -1)); \
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", (void *)NULL); \
- } \
- } while (0)
-#define ZIPFS_POSIX_ERROR(interp,errstr) \
- do { \
- if (interp) { \
- Tcl_SetObjResult(interp, Tcl_ObjPrintf( \
- "%s: %s", errstr, Tcl_PosixError(interp))); \
- } \
- } while (0)
-#define ZIPFS_ERROR_CODE(interp,errcode) \
- do { \
- if (interp) { \
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", errcode, (void *)NULL); \
- } \
- } while (0)
-
-#ifdef HAVE_ZLIB
-#include "zlib.h"
-#include "crypt.h"
-#include "zutil.h"
-#include "crc32.h"
-
-static const z_crc_t* crc32tab;
-
-/*
-** We are compiling as part of the core.
-** TIP430 style zipfs prefix
-*/
-
-#define ZIPFS_VOLUME "//zipfs:/"
-#define ZIPFS_ROOTDIR_DEPTH 3 /* Number of / in root mount */
-#define ZIPFS_VOLUME_LEN 9
-#define ZIPFS_APP_MOUNT ZIPFS_VOLUME "app"
-#define ZIPFS_ZIP_MOUNT ZIPFS_VOLUME "lib/tcl"
-#define ZIPFS_FALLBACK_ENCODING "cp437"
-
-/*
- * Various constants and offsets found in ZIP archive files
- */
-
-#define ZIP_SIG_LEN 4
-
-/*
- * Local header of ZIP archive member (at very beginning of each member).
- */
-
-#define ZIP_LOCAL_HEADER_SIG 0x04034b50
-#define ZIP_LOCAL_HEADER_LEN 30
-#define ZIP_LOCAL_SIG_OFFS 0
-#define ZIP_LOCAL_VERSION_OFFS 4
-#define ZIP_LOCAL_FLAGS_OFFS 6
-#define ZIP_LOCAL_COMPMETH_OFFS 8
-#define ZIP_LOCAL_MTIME_OFFS 10
-#define ZIP_LOCAL_MDATE_OFFS 12
-#define ZIP_LOCAL_CRC32_OFFS 14
-#define ZIP_LOCAL_COMPLEN_OFFS 18
-#define ZIP_LOCAL_UNCOMPLEN_OFFS 22
-#define ZIP_LOCAL_PATHLEN_OFFS 26
-#define ZIP_LOCAL_EXTRALEN_OFFS 28
-
-/*
- * Central header of ZIP archive member at end of ZIP file.
- */
-
-#define ZIP_CENTRAL_HEADER_SIG 0x02014b50
-#define ZIP_CENTRAL_HEADER_LEN 46
-#define ZIP_CENTRAL_SIG_OFFS 0
-#define ZIP_CENTRAL_VERSIONMADE_OFFS 4
-#define ZIP_CENTRAL_VERSION_OFFS 6
-#define ZIP_CENTRAL_FLAGS_OFFS 8
-#define ZIP_CENTRAL_COMPMETH_OFFS 10
-#define ZIP_CENTRAL_MTIME_OFFS 12
-#define ZIP_CENTRAL_MDATE_OFFS 14
-#define ZIP_CENTRAL_CRC32_OFFS 16
-#define ZIP_CENTRAL_COMPLEN_OFFS 20
-#define ZIP_CENTRAL_UNCOMPLEN_OFFS 24
-#define ZIP_CENTRAL_PATHLEN_OFFS 28
-#define ZIP_CENTRAL_EXTRALEN_OFFS 30
-#define ZIP_CENTRAL_FCOMMENTLEN_OFFS 32
-#define ZIP_CENTRAL_DISKFILE_OFFS 34
-#define ZIP_CENTRAL_IATTR_OFFS 36
-#define ZIP_CENTRAL_EATTR_OFFS 38
-#define ZIP_CENTRAL_LOCALHDR_OFFS 42
-
-/*
- * Central end signature at very end of ZIP file.
- */
-
-#define ZIP_CENTRAL_END_SIG 0x06054b50
-#define ZIP_CENTRAL_END_LEN 22
-#define ZIP_CENTRAL_END_SIG_OFFS 0
-#define ZIP_CENTRAL_DISKNO_OFFS 4
-#define ZIP_CENTRAL_DISKDIR_OFFS 6
-#define ZIP_CENTRAL_ENTS_OFFS 8
-#define ZIP_CENTRAL_TOTALENTS_OFFS 10
-#define ZIP_CENTRAL_DIRSIZE_OFFS 12
-#define ZIP_CENTRAL_DIRSTART_OFFS 16
-#define ZIP_CENTRAL_COMMENTLEN_OFFS 20
-
-#define ZIP_MIN_VERSION 20
-#define ZIP_COMPMETH_STORED 0
-#define ZIP_COMPMETH_DEFLATED 8
-
-#define ZIP_PASSWORD_END_SIG 0x5a5a4b50
-#define ZIP_CRYPT_HDR_LEN 12
-
-#define ZIP_MAX_FILE_SIZE INT_MAX
-#define DEFAULT_WRITE_MAX_SIZE ZIP_MAX_FILE_SIZE
-
-/*
- * Mutex to protect localtime(3) when no reentrant version available.
- */
-
-#if !defined(_WIN32) && !defined(HAVE_LOCALTIME_R) && TCL_THREADS
-TCL_DECLARE_MUTEX(localtimeMutex)
-#endif /* !_WIN32 && !HAVE_LOCALTIME_R && TCL_THREADS */
-
-/*
- * Forward declaration.
- */
-
-struct ZipEntry;
-
-/*
- * In-core description of mounted ZIP archive file.
- */
-
-typedef struct ZipFile {
- char *name; /* Archive name */
- size_t nameLength; /* Length of archive name */
- char isMemBuffer; /* When true, not a file but a memory buffer */
- Tcl_Channel chan; /* Channel handle or NULL */
- unsigned char *data; /* Memory mapped or malloc'ed file */
- size_t length; /* Length of memory mapped file */
- void *ptrToFree; /* Non-NULL if malloc'ed file */
- size_t numFiles; /* Number of files in archive */
- size_t baseOffset; /* Archive start */
- size_t passOffset; /* Password start */
- size_t directoryOffset; /* Archive directory start */
- size_t directorySize; /* Size of archive directory */
- unsigned char passBuf[264]; /* Password buffer */
- size_t numOpen; /* Number of open files on archive */
- struct ZipEntry *entries; /* List of files in archive */
- struct ZipEntry *topEnts; /* List of top-level dirs in archive */
- char *mountPoint; /* Mount point name */
- Tcl_Size mountPointLen; /* Length of mount point name */
-#ifdef _WIN32
- HANDLE mountHandle; /* Handle used for direct file access. */
-#endif /* _WIN32 */
-} ZipFile;
-
-/*
- * In-core description of file contained in mounted ZIP archive.
- */
-
-typedef struct ZipEntry {
- char *name; /* The full pathname of the virtual file */
- ZipFile *zipFilePtr; /* The ZIP file holding this virtual file */
- size_t offset; /* Data offset into memory mapped ZIP file */
- int numBytes; /* Uncompressed size of the virtual file.
- -1 for zip64 */
- int numCompressedBytes; /* Compressed size of the virtual file.
- -1 for zip64 */
- int compressMethod; /* Compress method */
- int isDirectory; /* 0 if file, 1 if directory, -1 if root */
- int depth; /* Number of slashes in path. */
- int crc32; /* CRC-32 as stored in ZIP */
- int timestamp; /* Modification time */
- int isEncrypted; /* True if data is encrypted */
- int flags;
-#define ZE_F_CRC_COMPARED 0x0001 /* If 1, the CRC has been compared. */
-#define ZE_F_CRC_CORRECT 0x0002 /* Only meaningful if ZE_F_CRC_COMPARED is 1 */
-#define ZE_F_VOLUME 0x0004 /* Entry corresponds to //zipfs:/ */
- unsigned char *data; /* File data if written */
- struct ZipEntry *next; /* Next file in the same archive */
- struct ZipEntry *tnext; /* Next top-level dir in archive */
-} ZipEntry;
-
-/*
- * File channel for file contained in mounted ZIP archive.
- *
- * Regarding data buffers:
- * For READ-ONLY files that are not encrypted and not compressed (zip STORE
- * method), ubuf points directly to the mapped zip file data in memory. No
- * additional storage is allocated and so ubufToFree is NULL.
- *
- * In all other combinations of compression and encryption or if channel is
- * writable, storage is allocated for the decrypted and/or uncompressed data
- * and a pointer to it is stored in ubufToFree and ubuf. When channel is
- * closed, ubufToFree is freed if not NULL. ubuf is irrelevant since it may
- * or may not point to allocated storage as above.
- */
-
-typedef struct ZipChannel {
- ZipFile *zipFilePtr; /* The ZIP file holding this channel */
- ZipEntry *zipEntryPtr; /* Pointer back to virtual file */
- Tcl_Size maxWrite; /* Maximum size for write */
- Tcl_Size numBytes; /* Number of bytes of uncompressed data */
- Tcl_Size cursor; /* Seek position for next read or write*/
- unsigned char *ubuf; /* Pointer to the uncompressed data */
- unsigned char *ubufToFree; /* NULL if ubuf points to memory that does not
- need freeing. Else memory to free (ubuf
- may point *inside* the block) */
- Tcl_Size ubufSize; /* Size of allocated ubufToFree */
- int iscompr; /* True if data is compressed */
- int isDirectory; /* Set to 1 if directory, or -1 if root */
- int isEncrypted; /* True if data is encrypted */
- int mode; /* O_WRITE, O_APPEND, O_TRUNC etc.*/
- unsigned long keys[3]; /* Key for decryption */
-} ZipChannel;
-static inline int ZipChannelWritable(ZipChannel *info) {
- return (info->mode & (O_WRONLY | O_RDWR)) != 0;
-}
-
-/*
- * Global variables.
- *
- * Most are kept in single ZipFS struct. When build with threading support
- * this struct is protected by the ZipFSMutex (see below).
- *
- * The "fileHash" component is the process-wide global table of all known ZIP
- * archive members in all mounted ZIP archives.
- *
- * The "zipHash" components is the process wide global table of all mounted
- * ZIP archive files.
- */
-
-static struct {
- int initialized; /* True when initialized */
- int lock; /* RW lock, see below */
- int waiters; /* RW lock, see below */
- int wrmax; /* Maximum write size of a file; only written
- * to from Tcl code in a trusted interpreter,
- * so NOT protected by mutex. */
- char *fallbackEntryEncoding;/* The fallback encoding for ZIP entries when
- * they are believed to not be UTF-8; only
- * written to from Tcl code in a trusted
- * interpreter, so not protected by mutex. */
- int idCount; /* Counter for channel names */
- Tcl_HashTable fileHash; /* File name to ZipEntry mapping */
- Tcl_HashTable zipHash; /* Mount to ZipFile mapping */
-} ZipFS = {
- 0, 0, 0, DEFAULT_WRITE_MAX_SIZE, NULL, 0,
- {0,{0,0,0,0},0,0,0,0,0,0,0,0,0},
- {0,{0,0,0,0},0,0,0,0,0,0,0,0,0}
-};
-
-/*
- * For password rotation.
- */
-
-static const char pwrot[17] =
- "\x00\x80\x40\xC0\x20\xA0\x60\xE0"
- "\x10\x90\x50\xD0\x30\xB0\x70\xF0";
-
-static const char *zipfs_literal_tcl_library = NULL;
-
-/* Function prototypes */
-
-static int CopyImageFile(Tcl_Interp *interp, const char *imgName,
- Tcl_Channel out);
-static int DescribeMounted(Tcl_Interp *interp,
- const char *mountPoint);
-static int InitReadableChannel(Tcl_Interp *interp,
- ZipChannel *info, ZipEntry *z);
-static int InitWritableChannel(Tcl_Interp *interp,
- ZipChannel *info, ZipEntry *z, int trunc);
-static int ListMountPoints(Tcl_Interp *interp);
-static int ContainsMountPoint(const char *path, int pathLen);
-static void CleanupMount(ZipFile *zf);
-static Tcl_Obj * ScriptLibrarySetup(const char *dirName);
-static void SerializeCentralDirectoryEntry(
- const unsigned char *start,
- const unsigned char *end, unsigned char *buf,
- ZipEntry *z, size_t nameLength);
-static void SerializeCentralDirectorySuffix(
- const unsigned char *start,
- const unsigned char *end, unsigned char *buf,
- int entryCount, long long directoryStartOffset,
- long long suffixStartOffset);
-static void SerializeLocalEntryHeader(
- const unsigned char *start,
- const unsigned char *end, unsigned char *buf,
- ZipEntry *z, int nameLength, int align);
-static int IsCryptHeaderValid(ZipEntry *z,
- unsigned char cryptHdr[ZIP_CRYPT_HDR_LEN]);
-static int DecodeCryptHeader(Tcl_Interp *interp, ZipEntry *z,
- unsigned long keys[3],
- unsigned char cryptHdr[ZIP_CRYPT_HDR_LEN]);
-#if !defined(STATIC_BUILD)
-static int ZipfsAppHookFindTclInit(const char *archive);
-#endif
-static int ZipFSPathInFilesystemProc(Tcl_Obj *pathPtr,
- void **clientDataPtr);
-static Tcl_Obj * ZipFSFilesystemPathTypeProc(Tcl_Obj *pathPtr);
-static Tcl_Obj * ZipFSFilesystemSeparatorProc(Tcl_Obj *pathPtr);
-static int ZipFSStatProc(Tcl_Obj *pathPtr, Tcl_StatBuf *buf);
-static int ZipFSAccessProc(Tcl_Obj *pathPtr, int mode);
-static Tcl_Channel ZipFSOpenFileChannelProc(Tcl_Interp *interp,
- Tcl_Obj *pathPtr, int mode, int permissions);
-static int ZipFSMatchInDirectoryProc(Tcl_Interp *interp,
- Tcl_Obj *result, Tcl_Obj *pathPtr,
- const char *pattern, Tcl_GlobTypeData *types);
-static void ZipFSMatchMountPoints(Tcl_Obj *result,
- Tcl_Obj *normPathPtr, const char *pattern,
- Tcl_DString *prefix);
-static Tcl_Obj * ZipFSListVolumesProc(void);
-static const char *const *ZipFSFileAttrStringsProc(Tcl_Obj *pathPtr,
- Tcl_Obj **objPtrRef);
-static int ZipFSFileAttrsGetProc(Tcl_Interp *interp, int index,
- Tcl_Obj *pathPtr, Tcl_Obj **objPtrRef);
-static int ZipFSFileAttrsSetProc(Tcl_Interp *interp, int index,
- Tcl_Obj *pathPtr, Tcl_Obj *objPtr);
-static int ZipFSLoadFile(Tcl_Interp *interp, Tcl_Obj *path,
- Tcl_LoadHandle *loadHandle,
- Tcl_FSUnloadFileProc **unloadProcPtr, int flags);
-static int ZipMapArchive(Tcl_Interp *interp, ZipFile *zf,
- void *handle);
-static void ZipfsSetup(void);
-static int ZipChannelClose(void *instanceData,
- Tcl_Interp *interp, int flags);
-static Tcl_DriverGetHandleProc ZipChannelGetFile;
-static int ZipChannelRead(void *instanceData, char *buf,
- int toRead, int *errloc);
-#if !defined(TCL_NO_DEPRECATED) && (TCL_MAJOR_VERSION < 9)
-static int ZipChannelSeek(void *instanceData, long offset,
- int mode, int *errloc);
-#endif
-static long long ZipChannelWideSeek(void *instanceData,
- long long offset, int mode, int *errloc);
-static void ZipChannelWatchChannel(void *instanceData,
- int mask);
-static int ZipChannelWrite(void *instanceData,
- const char *buf, int toWrite, int *errloc);
-
-/*
- * Define the ZIP filesystem dispatch table.
- */
-
-static const Tcl_Filesystem zipfsFilesystem = {
- "zipfs",
- sizeof(Tcl_Filesystem),
- TCL_FILESYSTEM_VERSION_2,
- ZipFSPathInFilesystemProc,
- NULL, /* dupInternalRepProc */
- NULL, /* freeInternalRepProc */
- NULL, /* internalToNormalizedProc */
- NULL, /* createInternalRepProc */
- NULL, /* normalizePathProc */
- ZipFSFilesystemPathTypeProc,
- ZipFSFilesystemSeparatorProc,
- ZipFSStatProc,
- ZipFSAccessProc,
- ZipFSOpenFileChannelProc,
- ZipFSMatchInDirectoryProc,
- NULL, /* utimeProc */
- NULL, /* linkProc */
- ZipFSListVolumesProc,
- ZipFSFileAttrStringsProc,
- ZipFSFileAttrsGetProc,
- ZipFSFileAttrsSetProc,
- NULL, /* createDirectoryProc */
- NULL, /* removeDirectoryProc */
- NULL, /* deleteFileProc */
- NULL, /* copyFileProc */
- NULL, /* renameFileProc */
- NULL, /* copyDirectoryProc */
- NULL, /* lstatProc */
- (Tcl_FSLoadFileProc *) (void *) ZipFSLoadFile,
- NULL, /* getCwdProc */
- NULL, /* chdirProc */
-};
-
-/*
- * The channel type/driver definition used for ZIP archive members.
- */
-
-static Tcl_ChannelType ZipChannelType = {
- "zip", /* Type name. */
- TCL_CHANNEL_VERSION_5,
- TCL_CLOSE2PROC, /* Close channel, clean instance data */
- ZipChannelRead, /* Handle read request */
- ZipChannelWrite, /* Handle write request */
-#if !defined(TCL_NO_DEPRECATED) && (TCL_MAJOR_VERSION < 9)
- ZipChannelSeek, /* Move location of access point, NULL'able */
-#else
- NULL, /* Move location of access point, NULL'able */
-#endif
- NULL, /* Set options, NULL'able */
- NULL, /* Get options, NULL'able */
- ZipChannelWatchChannel, /* Initialize notifier */
- ZipChannelGetFile, /* Get OS handle from the channel */
- ZipChannelClose, /* 2nd version of close channel, NULL'able */
- NULL, /* Set blocking mode for raw channel,
- * NULL'able */
- NULL, /* Function to flush channel, NULL'able */
- NULL, /* Function to handle event, NULL'able */
- ZipChannelWideSeek, /* Wide seek function, NULL'able */
- NULL, /* Thread action function, NULL'able */
- NULL, /* Truncate function, NULL'able */
-};
-
-/*
- * Miscellaneous constants.
- */
-
-#define ERROR_LENGTH ((size_t) -1)
-
-/*
- *------------------------------------------------------------------------
- *
- * TclIsZipfsPath --
- *
- * Checks if the passed path has a zipfs volume prefix.
- *
- * Results:
- * 0 if not a zipfs path
- * else the length of the zipfs volume prefix
- *
- * Side effects:
- * None.
- *
- *------------------------------------------------------------------------
- */
-int TclIsZipfsPath (const char *path)
-{
-#ifdef _WIN32
- return strncmp(path, ZIPFS_VOLUME, ZIPFS_VOLUME_LEN) ? 0 : ZIPFS_VOLUME_LEN;
-#else
- int i;
- for (i = 0; i < ZIPFS_VOLUME_LEN; ++i) {
- if (path[i] != ZIPFS_VOLUME[i] &&
- (path[i] != '\\' || ZIPFS_VOLUME[i] != '/')) {
- return 0;
- }
- }
- return ZIPFS_VOLUME_LEN;
-#endif
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipReadInt, ZipReadShort, ZipWriteInt, ZipWriteShort --
- *
- * Inline functions to read and write little-endian 16 and 32 bit
- * integers from/to buffers representing parts of ZIP archives.
- *
- * These take bufferStart and bufferEnd pointers, which are used to
- * maintain a guarantee that out-of-bounds accesses don't happen when
- * reading or writing critical directory structures.
- *
- *-------------------------------------------------------------------------
- */
-
-static inline unsigned int
-ZipReadInt(
- const unsigned char *bufferStart,
- const unsigned char *bufferEnd,
- const unsigned char *ptr)
-{
- if (ptr < bufferStart || ptr + 4 > bufferEnd) {
- Tcl_Panic("out of bounds read(4): start=%p, end=%p, ptr=%p",
- bufferStart, bufferEnd, ptr);
- }
- return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) |
- ((unsigned int)ptr[3] << 24);
-}
-
-static inline unsigned short
-ZipReadShort(
- const unsigned char *bufferStart,
- const unsigned char *bufferEnd,
- const unsigned char *ptr)
-{
- if (ptr < bufferStart || ptr + 2 > bufferEnd) {
- Tcl_Panic("out of bounds read(2): start=%p, end=%p, ptr=%p",
- bufferStart, bufferEnd, ptr);
- }
- return ptr[0] | (ptr[1] << 8);
-}
-
-static inline void
-ZipWriteInt(
- const unsigned char *bufferStart,
- const unsigned char *bufferEnd,
- unsigned char *ptr,
- unsigned int value)
-{
- if (ptr < bufferStart || ptr + 4 > bufferEnd) {
- Tcl_Panic("out of bounds write(4): start=%p, end=%p, ptr=%p",
- bufferStart, bufferEnd, ptr);
- }
- ptr[0] = value & 0xff;
- ptr[1] = (value >> 8) & 0xff;
- ptr[2] = (value >> 16) & 0xff;
- ptr[3] = (value >> 24) & 0xff;
-}
-
-static inline void
-ZipWriteShort(
- const unsigned char *bufferStart,
- const unsigned char *bufferEnd,
- unsigned char *ptr,
- unsigned short value)
-{
- if (ptr < bufferStart || ptr + 2 > bufferEnd) {
- Tcl_Panic("out of bounds write(2): start=%p, end=%p, ptr=%p",
- bufferStart, bufferEnd, ptr);
- }
- ptr[0] = value & 0xff;
- ptr[1] = (value >> 8) & 0xff;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ReadLock, WriteLock, Unlock --
- *
- * POSIX like rwlock functions to support multiple readers and single
- * writer on internal structs.
- *
- * Limitations:
- * - a read lock cannot be promoted to a write lock
- * - a write lock may not be nested
- *
- *-------------------------------------------------------------------------
- */
-
-TCL_DECLARE_MUTEX(ZipFSMutex)
-
-#if TCL_THREADS
-
-static Tcl_Condition ZipFSCond;
-
-static inline void
-ReadLock(void)
-{
- Tcl_MutexLock(&ZipFSMutex);
- while (ZipFS.lock < 0) {
- ZipFS.waiters++;
- Tcl_ConditionWait(&ZipFSCond, &ZipFSMutex, NULL);
- ZipFS.waiters--;
- }
- ZipFS.lock++;
- Tcl_MutexUnlock(&ZipFSMutex);
-}
-
-static inline void
-WriteLock(void)
-{
- Tcl_MutexLock(&ZipFSMutex);
- while (ZipFS.lock != 0) {
- ZipFS.waiters++;
- Tcl_ConditionWait(&ZipFSCond, &ZipFSMutex, NULL);
- ZipFS.waiters--;
- }
- ZipFS.lock = -1;
- Tcl_MutexUnlock(&ZipFSMutex);
-}
-
-static inline void
-Unlock(void)
-{
- Tcl_MutexLock(&ZipFSMutex);
- if (ZipFS.lock > 0) {
- --ZipFS.lock;
- } else if (ZipFS.lock < 0) {
- ZipFS.lock = 0;
- }
- if ((ZipFS.lock == 0) && (ZipFS.waiters > 0)) {
- Tcl_ConditionNotify(&ZipFSCond);
- }
- Tcl_MutexUnlock(&ZipFSMutex);
-}
-
-#else /* !TCL_THREADS */
-#define ReadLock() do {} while (0)
-#define WriteLock() do {} while (0)
-#define Unlock() do {} while (0)
-#endif /* TCL_THREADS */
-
-/*
- *-------------------------------------------------------------------------
- *
- * DosTimeDate, ToDosTime, ToDosDate --
- *
- * Functions to perform conversions between DOS time stamps and POSIX
- * time_t.
- *
- *-------------------------------------------------------------------------
- */
-
-static time_t
-DosTimeDate(
- int dosDate,
- int dosTime)
-{
- struct tm tm;
- time_t ret;
-
- memset(&tm, 0, sizeof(tm));
- tm.tm_isdst = -1; /* let mktime() deal with DST */
- tm.tm_year = ((dosDate & 0xfe00) >> 9) + 80;
- tm.tm_mon = ((dosDate & 0x1e0) >> 5) - 1;
- tm.tm_mday = dosDate & 0x1f;
- tm.tm_hour = (dosTime & 0xf800) >> 11;
- tm.tm_min = (dosTime & 0x7e0) >> 5;
- tm.tm_sec = (dosTime & 0x1f) << 1;
- ret = mktime(&tm);
- if (ret == (time_t) -1) {
- /* fallback to 1980-01-01T00:00:00+00:00 (DOS epoch) */
- ret = (time_t) 315532800;
- }
- return ret;
-}
-
-static int
-ToDosTime(
- time_t when)
-{
- struct tm *tmp, tm;
-
-#if !TCL_THREADS || defined(_WIN32)
- /* Not threaded, or on Win32 which uses thread local storage */
- tmp = localtime(&when);
- tm = *tmp;
-#elif defined(HAVE_LOCALTIME_R)
- /* Threaded, have reentrant API */
- tmp = &tm;
- localtime_r(&when, tmp);
-#else /* TCL_THREADS && !_WIN32 && !HAVE_LOCALTIME_R */
- /* Only using a mutex is safe. */
- Tcl_MutexLock(&localtimeMutex);
- tmp = localtime(&when);
- tm = *tmp;
- Tcl_MutexUnlock(&localtimeMutex);
-#endif
- return (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
-}
-
-static int
-ToDosDate(
- time_t when)
-{
- struct tm *tmp, tm;
-
-#if !TCL_THREADS || defined(_WIN32)
- /* Not threaded, or on Win32 which uses thread local storage */
- tmp = localtime(&when);
- tm = *tmp;
-#elif /* TCL_THREADS && !_WIN32 && */ defined(HAVE_LOCALTIME_R)
- /* Threaded, have reentrant API */
- tmp = &tm;
- localtime_r(&when, tmp);
-#else /* TCL_THREADS && !_WIN32 && !HAVE_LOCALTIME_R */
- /* Only using a mutex is safe. */
- Tcl_MutexLock(&localtimeMutex);
- tmp = localtime(&when);
- tm = *tmp;
- Tcl_MutexUnlock(&localtimeMutex);
-#endif
- return ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * CountSlashes --
- *
- * This function counts the number of slashes in a pathname string.
- *
- * Results:
- * Number of slashes found in string.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static inline size_t
-CountSlashes(
- const char *string)
-{
- size_t count = 0;
- const char *p = string;
-
- while (*p != '\0') {
- if (*p == '/') {
- count++;
- }
- p++;
- }
- return count;
-}
-
-/*
- *------------------------------------------------------------------------
- *
- * IsCryptHeaderValid --
- *
- * Computes the validity of the encryption header CRC for a ZipEntry.
- *
- * Results:
- * Returns 1 if the header is valid else 0.
- *
- * Side effects:
- * None.
- *
- *------------------------------------------------------------------------
- */
-static int IsCryptHeaderValid(
- ZipEntry *z,
- unsigned char cryptHeader[ZIP_CRYPT_HDR_LEN]
- )
-{
- /*
- * There are multiple possibilities. The last one or two bytes of the
- * encryption header should match the last one or two bytes of the
- * CRC of the file. Or the last byte of the encryption header should
- * be the high order byte of the file time. Depending on the archiver
- * and version, any of the might be in used. We follow libzip in checking
- * only one byte against both the crc and the time. Note that by design
- * the check generates high number of false positives in any case.
- * Also, in case a check is passed when it should not, the final CRC
- * calculation will (should) catch it. Only difference is it will be
- * reported as a corruption error instead of incorrect password.
- */
- int dosTime = ToDosTime(z->timestamp);
- if (cryptHeader[11] == (unsigned char)(dosTime >> 8)) {
- /* Infozip style - Tested with test-password.zip */
- return 1;
- }
- /* DOS time did not match, may be CRC does */
- if (z->crc32) {
- /* Pkware style - Tested with test-password2.zip */
- return (cryptHeader[11] == (unsigned char)(z->crc32 >> 24));
- }
-
- /* No CRC, no way to verify. Assume valid */
- return 1;
-}
-
-/*
- *------------------------------------------------------------------------
- *
- * DecodeCryptHeader --
- *
- * Decodes the crypt header and validates it.
- *
- * Results:
- * TCL_OK on success, TCL_ERROR on failure.
- *
- * Side effects:
- * On success, keys[] are updated. On failure, an error message is
- * left in interp if not NULL.
- *
- *------------------------------------------------------------------------
- */
-static int
-DecodeCryptHeader(Tcl_Interp *interp,
- ZipEntry *z,
- unsigned long keys[3],/* Updated on success. Must have been
- initialized by caller. */
- unsigned char cryptHeader[ZIP_CRYPT_HDR_LEN]) /* From zip file content */
-{
- int i;
- int ch;
- int len = z->zipFilePtr->passBuf[0] & 0xFF;
- char passBuf[260];
-
- for (i = 0; i < len; i++) {
- ch = z->zipFilePtr->passBuf[len - i];
- passBuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
- }
- passBuf[i] = '\0';
- init_keys(passBuf, keys, crc32tab);
- memset(passBuf, 0, sizeof(passBuf));
- unsigned char encheader[ZIP_CRYPT_HDR_LEN];
- memcpy(encheader, cryptHeader, ZIP_CRYPT_HDR_LEN);
- for (i = 0; i < ZIP_CRYPT_HDR_LEN; i++) {
- ch = cryptHeader[i];
- ch ^= decrypt_byte(keys, crc32tab);
- encheader[i] = ch;
- update_keys(keys, crc32tab, ch);
- }
- if (!IsCryptHeaderValid(z, encheader)) {
- ZIPFS_ERROR(interp, "invalid password");
- ZIPFS_ERROR_CODE(interp, "PASSWORD");
- return TCL_ERROR;
- }
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * DecodeZipEntryText --
- *
- * Given a sequence of bytes from an entry in a ZIP central directory,
- * convert that into a Tcl string. This is complicated because we don't
- * actually know what encoding is in use! So we try to use UTF-8, and if
- * that goes wrong, we fall back to a user-specified encoding, or to an
- * encoding we specify (Windows code page 437), or to ISO 8859-1 if
- * absolutely nothing else works.
- *
- * During Tcl startup, we skip the user-specified encoding and cp437, as
- * we may well not have any loadable encodings yet. Tcl's own library
- * files ought to be using ASCII filenames.
- *
- * Results:
- * The decoded filename; the filename is owned by the argument DString.
- *
- * Side effects:
- * Updates dstPtr.
- *
- *-------------------------------------------------------------------------
- */
-
-static char *
-DecodeZipEntryText(
- const unsigned char *inputBytes,
- unsigned int inputLength,
- Tcl_DString *dstPtr) /* Must have been initialized by caller! */
-{
- Tcl_Encoding encoding;
- const char *src;
- char *dst;
- int dstLen, srcLen = inputLength, flags;
- Tcl_EncodingState state;
-
- if (inputLength < 1) {
- return Tcl_DStringValue(dstPtr);
- }
-
- /*
- * We can't use Tcl_ExternalToUtfDString at this point; it has no way to
- * fail. So we use this modified version of it that can report encoding
- * errors to us (so we can fall back to something else).
- *
- * The utf-8 encoding is implemented internally, and so is guaranteed to
- * be present.
- */
-
- src = (const char *) inputBytes;
- dst = Tcl_DStringValue(dstPtr);
- dstLen = dstPtr->spaceAvl - 1;
- flags = TCL_ENCODING_START | TCL_ENCODING_END |
- TCL_ENCODING_PROFILE_STRICT; /* Special flag! */
-
- while (1) {
- int srcRead, dstWrote;
- int result = Tcl_ExternalToUtf(NULL, tclUtf8Encoding, src, srcLen, flags,
- &state, dst, dstLen, &srcRead, &dstWrote, NULL);
- int soFar = dst + dstWrote - Tcl_DStringValue(dstPtr);
-
- if (result == TCL_OK) {
- Tcl_DStringSetLength(dstPtr, soFar);
- return Tcl_DStringValue(dstPtr);
- } else if (result != TCL_CONVERT_NOSPACE) {
- break;
- }
-
- flags &= ~TCL_ENCODING_START;
- src += srcRead;
- srcLen -= srcRead;
- if (Tcl_DStringLength(dstPtr) == 0) {
- Tcl_DStringSetLength(dstPtr, dstLen);
- }
- Tcl_DStringSetLength(dstPtr, 2 * Tcl_DStringLength(dstPtr) + 1);
- dst = Tcl_DStringValue(dstPtr) + soFar;
- dstLen = Tcl_DStringLength(dstPtr) - soFar - 1;
- }
-
- /*
- * Something went wrong. Fall back to another encoding. Those *can* use
- * Tcl_ExternalToUtfDString().
- */
-
- encoding = NULL;
- if (ZipFS.fallbackEntryEncoding) {
- encoding = Tcl_GetEncoding(NULL, ZipFS.fallbackEntryEncoding);
- }
- if (!encoding) {
- encoding = Tcl_GetEncoding(NULL, ZIPFS_FALLBACK_ENCODING);
- }
- if (!encoding) {
- /*
- * Fallback to internal encoding that always converts all bytes.
- * Should only happen when a filename isn't UTF-8 and we've not got
- * our encodings initialised for some reason.
- */
-
- encoding = Tcl_GetEncoding(NULL, "iso8859-1");
- }
-
- char *converted = Tcl_ExternalToUtfDString(encoding,
- (const char *) inputBytes, inputLength, dstPtr);
- Tcl_FreeEncoding(encoding);
- return converted;
-}
-
-/*
- *------------------------------------------------------------------------
- *
- * NormalizeMountPoint --
- *
- * Converts the passed path into a normalized zipfs mount point
- * of the form //zipfs:/some/path. On Windows any \ path separators
- * are converted to /.
- *
- * Mount points with a volume will raise an error unless the volume is
- * zipfs root. Thus D:/foo is not a valid mount point.
- *
- * Relative paths and absolute paths without a volume are mapped under
- * the zipfs root.
- *
- * The empty string is mapped to the zipfs root.
- *
- * dsPtr is initialized by the function and must be cleared by caller
- * on a successful return.
- *
- * Results:
- * TCL_OK on success with normalized mount path in dsPtr
- * TCL_ERROR on fail with error message in interp if not NULL
- *
- *------------------------------------------------------------------------
- */
-static int
-NormalizeMountPoint(Tcl_Interp *interp,
- const char *mountPath,
- Tcl_DString *dsPtr) /* Must be initialized by caller! */
-{
- const char *joiner[2];
- char *joinedPath;
- Tcl_Obj *unnormalizedObj;
- Tcl_Obj *normalizedObj;
- const char *normalizedPath;
- Tcl_Size normalizedLen;
- Tcl_DString dsJoin;
-
- /*
- * Several things need to happen here
- * - Absolute paths containing volumes (drive letter or UNC) raise error
- * except of course if the volume is zipfs root
- * - \ -> / and // -> / conversions (except if UNC which is error)
- * - . and .. have to be dealt with
- * The first is explicitly checked, the others are dealt with a
- * combination file join and normalize. Easier than doing it ourselves
- * and not performance sensitive anyways.
- */
-
- joiner[0] = ZIPFS_VOLUME;
- joiner[1] = mountPath;
- Tcl_DStringInit(&dsJoin);
- joinedPath = Tcl_JoinPath(2, joiner, &dsJoin);
-
- /* Now joinedPath has all \ -> / and // -> / (except UNC) converted. */
-
- if (!strncmp(ZIPFS_VOLUME, joinedPath, ZIPFS_VOLUME_LEN)) {
- unnormalizedObj = Tcl_DStringToObj(&dsJoin);
- } else {
- if (joinedPath[0] != '/' || joinedPath[1] == '/') {
- /* mount path was D:/x, D:x or //unc */
- goto invalidMountPath;
- }
- unnormalizedObj = Tcl_ObjPrintf(ZIPFS_VOLUME "%s", joinedPath + 1);
- }
- Tcl_IncrRefCount(unnormalizedObj);
- normalizedObj = Tcl_FSGetNormalizedPath(interp, unnormalizedObj);
- if (normalizedObj == NULL) {
- Tcl_DecrRefCount(unnormalizedObj);
- goto errorReturn;
- }
- Tcl_IncrRefCount(normalizedObj); /* BEFORE DecrRefCount on unnormalizedObj */
- Tcl_DecrRefCount(unnormalizedObj);
-
- /* normalizedObj owned by Tcl!! Do NOT DecrRef without an IncrRef */
- normalizedPath = TclGetStringFromObj(normalizedObj, &normalizedLen);
- Tcl_DStringFree(&dsJoin);
- Tcl_DStringAppend(dsPtr, normalizedPath, normalizedLen);
- Tcl_DecrRefCount(normalizedObj);
- return TCL_OK;
-
-invalidMountPath:
- if (interp) {
- Tcl_SetObjResult(interp,
- Tcl_ObjPrintf("Invalid mount path \"%s\"", mountPath));
- ZIPFS_ERROR_CODE(interp, "MOUNT_PATH");
- }
-
-errorReturn:
- Tcl_DStringFree(&dsJoin);
- return TCL_ERROR;
-}
-
-/*
- *------------------------------------------------------------------------
- *
- * MapPathToZipfs --
- *
- * Maps a path as stored in a zip archive to its normalized location
- * under a given zipfs mount point. Relative paths and Unix style
- * absolute paths go directly under the mount point. Volume relative
- * paths and absolute paths that have a volume (drive or UNC) are
- * stripped of the volume before joining the mount point.
- *
- * Results:
- * Pointer to normalized path.
- *
- * Side effects:
- * Stores mapped path in dsPtr.
- *
- *------------------------------------------------------------------------
- */
-static char *
-MapPathToZipfs(Tcl_Interp *interp,
- const char *mountPath, /* Must be fully normalized */
- const char *path, /* Archive content path to map */
- Tcl_DString *dsPtr) /* Must be initialized and cleared
- by caller */
-{
- const char *joiner[2];
- char *joinedPath;
- Tcl_Obj *unnormalizedObj;
- Tcl_Obj *normalizedObj;
- const char *normalizedPath;
- Tcl_Size normalizedLen;
- Tcl_DString dsJoin;
-
- assert(TclIsZipfsPath(mountPath));
-
- joiner[0] = mountPath;
- joiner[1] = path;
-#ifndef _WIN32
- /* On Unix C:/foo/bat is not treated as absolute by JoinPath so check ourself */
- if (path[0] && path[1] == ':') {
- joiner[1] += 2;
- }
-#endif
- Tcl_DStringInit(&dsJoin);
- joinedPath = Tcl_JoinPath(2, joiner, &dsJoin);
-
- if (strncmp(ZIPFS_VOLUME, joinedPath, ZIPFS_VOLUME_LEN)) {
- /* path was not relative. Strip off the volume (e.g. UNC) */
- Tcl_Size numParts;
- const char **partsPtr;
- Tcl_SplitPath(path, &numParts, &partsPtr);
- Tcl_DStringFree(&dsJoin);
- partsPtr[0] = mountPath;
- (void)Tcl_JoinPath(numParts, partsPtr, &dsJoin);
- ckfree(partsPtr);
- }
- unnormalizedObj = Tcl_DStringToObj(&dsJoin); /* Also resets dsJoin */
- Tcl_IncrRefCount(unnormalizedObj);
- normalizedObj = Tcl_FSGetNormalizedPath(interp, unnormalizedObj);
- if (normalizedObj == NULL) {
- /* Should not happen but continue... */
- normalizedObj = unnormalizedObj;
- }
- Tcl_IncrRefCount(normalizedObj); /* BEFORE DecrRefCount on unnormalizedObj */
- Tcl_DecrRefCount(unnormalizedObj);
-
- /* normalizedObj owned by Tcl!! Do NOT DecrRef without an IncrRef */
- normalizedPath = TclGetStringFromObj(normalizedObj, &normalizedLen);
- Tcl_DStringAppend(dsPtr, normalizedPath, normalizedLen);
- Tcl_DecrRefCount(normalizedObj);
- return Tcl_DStringValue(dsPtr);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSLookup --
- *
- * This function returns the ZIP entry struct corresponding to the ZIP
- * archive member of the given file name. Caller must hold the right
- * lock.
- *
- * Results:
- * Returns the pointer to ZIP entry struct or NULL if the the given file
- * name could not be found in the global list of ZIP archive members.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static inline ZipEntry *
-ZipFSLookup(
- const char *filename)
-{
- Tcl_HashEntry *hPtr;
- ZipEntry *z = NULL;
-
- hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, filename);
- if (hPtr) {
- z = (ZipEntry *) Tcl_GetHashValue(hPtr);
- }
- return z;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSLookupZip --
- *
- * This function gets the structure for a mounted ZIP archive.
- *
- * Results:
- * Returns a pointer to the structure, or NULL if the file is ZIP file is
- * unknown/not mounted.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static inline ZipFile *
-ZipFSLookupZip(
- const char *mountPoint)
-{
- Tcl_HashEntry *hPtr;
- ZipFile *zf = NULL;
-
- hPtr = Tcl_FindHashEntry(&ZipFS.zipHash, mountPoint);
- if (hPtr) {
- zf = (ZipFile *) Tcl_GetHashValue(hPtr);
- }
- return zf;
-}
-
-/*
- *------------------------------------------------------------------------
- *
- * ContainsMountPoint --
- *
- * Check if there is a mount point anywhere under the specified path.
- * Although the function will work for any path, for efficiency reasons
- * it should be called only after checking ZipFSLookup does not find
- * the path.
- *
- * Caller must hold read lock before calling.
- *
- * Results:
- * 1 - there is at least one mount point under the path
- * 0 - otherwise
- *
- * Side effects:
- * None.
- *
- *------------------------------------------------------------------------
- */
-static int
-ContainsMountPoint (const char *path, int pathLen)
-{
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch search;
-
- if (ZipFS.zipHash.numEntries == 0) {
- return 0;
- }
- if (pathLen < 0)
- pathLen = strlen(path);
-
- /*
- * We are looking for the case where the path is //zipfs:/a/b
- * and there is a mount point //zipfs:/a/b/c/.. below it
- */
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- ZipFile *zf = (ZipFile *) Tcl_GetHashValue(hPtr);
-
- if (zf->mountPointLen == 0) {
- /*
- * Enumerate the contents of the ZIP; it's mounted on the root.
- * TODO - a holdover from androwish? Tcl does not allow mounting
- * outside of the //zipfs:/ area.
- */
- ZipEntry *z;
-
- for (z = zf->topEnts; z; z = z->tnext) {
- int lenz = (int) strlen(z->name);
- if ((lenz >= pathLen) &&
- (z->name[pathLen] == '/' || z->name[pathLen] == '\0') &&
- (strncmp(z->name, path, pathLen) == 0)) {
- return 1;
- }
- }
- } else if ((zf->mountPointLen >= pathLen) &&
- (zf->mountPoint[pathLen] == '/' ||
- zf->mountPoint[pathLen] == '\0' ||
- pathLen == ZIPFS_VOLUME_LEN) &&
- (strncmp(zf->mountPoint, path, pathLen) == 0)) {
- /* Matched standard mount */
- return 1;
- }
- }
- return 0;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * AllocateZipFile, AllocateZipEntry, AllocateZipChannel --
- *
- * Allocates the memory for a datastructure. Always ensures that it is
- * zeroed out for safety.
- *
- * Returns:
- * The allocated structure, or NULL if allocate fails.
- *
- * Side effects:
- * The interpreter result may be written to on error. Which might fail
- * (for ZipFile) in a low-memory situation. Always panics if ZipEntry
- * allocation fails.
- *
- *-------------------------------------------------------------------------
- */
-
-static inline ZipFile *
-AllocateZipFile(
- Tcl_Interp *interp,
- size_t mountPointNameLength)
-{
- size_t size = sizeof(ZipFile) + mountPointNameLength + 1;
- ZipFile *zf = (ZipFile *) attemptckalloc(size);
-
- if (!zf) {
- ZIPFS_MEM_ERROR(interp);
- } else {
- memset(zf, 0, size);
- }
- return zf;
-}
-
-static inline ZipEntry *
-AllocateZipEntry(void)
-{
- ZipEntry *z = (ZipEntry *) ckalloc(sizeof(ZipEntry));
- memset(z, 0, sizeof(ZipEntry));
- return z;
-}
-
-static inline ZipChannel *
-AllocateZipChannel(
- Tcl_Interp *interp)
-{
- ZipChannel *zc = (ZipChannel *) attemptckalloc(sizeof(ZipChannel));
-
- if (!zc) {
- ZIPFS_MEM_ERROR(interp);
- } else {
- memset(zc, 0, sizeof(ZipChannel));
- }
- return zc;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSCloseArchive --
- *
- * This function closes a mounted ZIP archive file.
- *
- * Results:
- * None.
- *
- * Side effects:
- * A memory mapped ZIP archive is unmapped, allocated memory is released.
- * The ZipFile pointer is *NOT* deallocated by this function.
- *
- *-------------------------------------------------------------------------
- */
-
-static void
-ZipFSCloseArchive(
- Tcl_Interp *interp, /* Current interpreter. */
- ZipFile *zf)
-{
- if (zf->nameLength) {
- ckfree(zf->name);
- }
- if (zf->isMemBuffer) {
- /* Pointer to memory */
- if (zf->ptrToFree) {
- ckfree(zf->ptrToFree);
- zf->ptrToFree = NULL;
- }
- zf->data = NULL;
- return;
- }
-
- /*
- * Remove the memory mapping, if we have one.
- */
-
-#ifdef _WIN32
- if (zf->data && !zf->ptrToFree) {
- UnmapViewOfFile(zf->data);
- zf->data = NULL;
- }
- if (zf->mountHandle != INVALID_HANDLE_VALUE) {
- CloseHandle(zf->mountHandle);
- }
-#else /* !_WIN32 */
- if ((zf->data != MAP_FAILED) && !zf->ptrToFree) {
- munmap(zf->data, zf->length);
- zf->data = (unsigned char *) MAP_FAILED;
- }
-#endif /* _WIN32 */
-
- if (zf->ptrToFree) {
- ckfree(zf->ptrToFree);
- zf->ptrToFree = NULL;
- }
- if (zf->chan) {
- Tcl_Close(interp, zf->chan);
- zf->chan = NULL;
- }
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSFindTOC --
- *
- * This function takes a memory mapped zip file and indexes the contents.
- * When "needZip" is zero an embedded ZIP archive in an executable file
- * is accepted. Note that we do not support ZIP64.
- *
- * Results:
- * TCL_OK on success, TCL_ERROR otherwise with an error message placed
- * into the given "interp" if it is not NULL.
- *
- * Side effects:
- * The given ZipFile struct is filled with information about the ZIP
- * archive file. On error, ZipFSCloseArchive is called on zf but
- * it is not freed.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSFindTOC(
- Tcl_Interp *interp, /* Current interpreter. NULLable. */
- int needZip,
- ZipFile *zf)
-{
- size_t i, minoff;
- const unsigned char *eocdPtr; /* End of Central Directory Record */
- const unsigned char *start = zf->data;
- const unsigned char *end = zf->data + zf->length;
-
- /*
- * Scan backwards from the end of the file for the signature. This is
- * necessary because ZIP archives aren't the only things that get tagged
- * on the end of executables; digital signatures can also go there.
- */
-
- eocdPtr = zf->data + zf->length - ZIP_CENTRAL_END_LEN;
- while (eocdPtr >= start) {
- if (*eocdPtr == (ZIP_CENTRAL_END_SIG & 0xFF)) {
- if (ZipReadInt(start, end, eocdPtr) == ZIP_CENTRAL_END_SIG) {
- break;
- }
- eocdPtr -= ZIP_SIG_LEN;
- } else {
- --eocdPtr;
- }
- }
- if (eocdPtr < zf->data) {
- /*
- * Didn't find it (or not enough space for a central directory!); not
- * a ZIP archive. This might be OK or a problem.
- */
-
- if (!needZip) {
- zf->baseOffset = zf->passOffset = zf->length;
- return TCL_OK;
- }
- ZIPFS_ERROR(interp, "archive directory end signature not found");
- ZIPFS_ERROR_CODE(interp, "END_SIG");
-
- error:
- ZipFSCloseArchive(interp, zf);
- return TCL_ERROR;
-
- }
-
- /*
- * eocdPtr -> End of Central Directory (EOCD) record at this point.
- * Note this is not same as "end of Central Directory" :-) as EOCD
- * is a record/structure in the ZIP spec terminology
- */
-
- /*
- * How many files in the archive? If that's bogus, we're done here.
- */
-
- zf->numFiles = ZipReadShort(start, end, eocdPtr + ZIP_CENTRAL_ENTS_OFFS);
- if (zf->numFiles == 0) {
- if (!needZip) {
- zf->baseOffset = zf->passOffset = zf->length;
- return TCL_OK;
- }
- ZIPFS_ERROR(interp, "empty archive");
- ZIPFS_ERROR_CODE(interp, "EMPTY");
- goto error;
- }
-
- /*
- * The Central Directory (CD) is a series of Central Directory File
- * Header (CDFH) records preceding the EOCD (but not necessarily
- * immediately preceding). cdirZipOffset is the offset into the
- * *archive* to the CD (first CDFH). The size of the CD is given by
- * cdirSize. NOTE: offset into archive does NOT mean offset into
- * (zf->data) as other data may precede the archive in the file.
- */
- ptrdiff_t eocdDataOffset = eocdPtr - zf->data;
- unsigned int cdirZipOffset = ZipReadInt(start, end, eocdPtr + ZIP_CENTRAL_DIRSTART_OFFS);
- unsigned int cdirSize = ZipReadInt(start, end, eocdPtr + ZIP_CENTRAL_DIRSIZE_OFFS);
-
- /*
- * As computed above,
- * eocdDataOffset < zf->length.
- * In addition, the following consistency checks must be met
- * (1) cdirZipOffset <= eocdDataOffset (to prevent under flow in computation of (2))
- * (2) cdirZipOffset + cdirSize <= eocdDataOffset. Else the CD will be overlapping
- * the EOCD. Note this automatically means cdirZipOffset+cdirSize < zf->length.
- */
- if (!(cdirZipOffset <= (size_t)eocdDataOffset &&
- cdirSize <= eocdDataOffset - cdirZipOffset)) {
- if (!needZip) {
- /* Simply point to end od data */
- zf->directoryOffset = zf->baseOffset = zf->passOffset = zf->length;
- return TCL_OK;
- }
- ZIPFS_ERROR(interp, "archive directory truncated");
- ZIPFS_ERROR_CODE(interp, "NO_DIR");
- goto error;
- }
-
- /*
- * Calculate the offset of the CD in the *data*. If there was no extra
- * "junk" preceding the archive, this would just be cdirZipOffset but
- * otherwise we have to account for it.
- */
- if (eocdDataOffset - cdirSize > cdirZipOffset) {
- zf->baseOffset = eocdDataOffset - cdirSize - cdirZipOffset;
- } else {
- zf->baseOffset = 0;
- }
- zf->passOffset = zf->baseOffset;
- zf->directoryOffset = cdirZipOffset + zf->baseOffset;
- zf->directorySize = cdirSize;
-
- /*
- * Read the central directory.
- */
- const unsigned char *const cdirStart = eocdPtr - cdirSize; /* Start of CD */
- const unsigned char *dirEntry;
- minoff = zf->length;
- for (dirEntry = cdirStart, i = 0; i < zf->numFiles; i++) {
- if ((dirEntry-cdirStart) + ZIP_CENTRAL_HEADER_LEN > (ptrdiff_t)zf->directorySize) {
- ZIPFS_ERROR(interp, "truncated directory");
- ZIPFS_ERROR_CODE(interp, "TRUNC_DIR");
- goto error;
- }
- if (ZipReadInt(start, end, dirEntry) != ZIP_CENTRAL_HEADER_SIG) {
- ZIPFS_ERROR(interp, "wrong header signature");
- ZIPFS_ERROR_CODE(interp, "HDR_SIG");
- goto error;
- }
- int pathlen = ZipReadShort(start, end, dirEntry + ZIP_CENTRAL_PATHLEN_OFFS);
- int comlen = ZipReadShort(start, end, dirEntry + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
- int extra = ZipReadShort(start, end, dirEntry + ZIP_CENTRAL_EXTRALEN_OFFS);
- size_t localhdr_off = ZipReadInt(start, end, dirEntry + ZIP_CENTRAL_LOCALHDR_OFFS);
- const unsigned char *localP = zf->data + zf->baseOffset + localhdr_off;
- if (localP > (cdirStart - ZIP_LOCAL_HEADER_LEN) ||
- ZipReadInt(start, end, localP) != ZIP_LOCAL_HEADER_SIG) {
- ZIPFS_ERROR(interp, "Failed to find local header");
- ZIPFS_ERROR_CODE(interp, "LCL_HDR");
- goto error;
- }
- if (localhdr_off < minoff) {
- minoff = localhdr_off;
- }
- dirEntry += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN;
- }
- if ((dirEntry-cdirStart) < (ptrdiff_t) zf->directorySize) {
- /* file count and dir size do not match */
- ZIPFS_ERROR(interp, "short file count");
- ZIPFS_ERROR_CODE(interp, "FILE_COUNT");
- goto error;
- }
-
- zf->passOffset = minoff + zf->baseOffset;
-
- /*
- * If there's also an encoded password, extract that too (but don't decode
- * yet).
- * TODO - is this even part of the ZIP "standard". The idea of storing
- * a password with the archive seems absurd, encoded or not.
- */
-
- unsigned char *q = zf->data + zf->passOffset;
- if ((zf->passOffset >= 6) && (start < q-4) &&
- (ZipReadInt(start, end, q - 4) == ZIP_PASSWORD_END_SIG)) {
- const unsigned char *passPtr;
-
- i = q[-5];
- passPtr = q - 5 - i;
- if (passPtr >= start && passPtr + i < end) {
- zf->passBuf[0] = i;
- memcpy(zf->passBuf + 1, passPtr, i);
- zf->passOffset -= i ? (5 + i) : 0;
- }
- }
-
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSOpenArchive --
- *
- * This function opens a ZIP archive file for reading. An attempt is made
- * to memory map that file. Otherwise it is read into an allocated memory
- * buffer. The ZIP archive header is verified and must be valid for the
- * function to succeed. When "needZip" is zero an embedded ZIP archive in
- * an executable file is accepted.
- *
- * Results:
- * TCL_OK on success, TCL_ERROR otherwise with an error message placed
- * into the given "interp" if it is not NULL. On error, ZipFSCloseArchive
- * is called on zf but it is not freed.
- *
- * Side effects:
- * ZIP archive is memory mapped or read into allocated memory, the given
- * ZipFile struct is filled with information about the ZIP archive file.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSOpenArchive(
- Tcl_Interp *interp, /* Current interpreter. NULLable. */
- const char *zipname, /* Path to ZIP file to open. */
- int needZip,
- ZipFile *zf)
-{
- size_t i;
- void *handle;
-
- zf->nameLength = 0;
- zf->isMemBuffer = 0;
-#ifdef _WIN32
- zf->data = NULL;
- zf->mountHandle = INVALID_HANDLE_VALUE;
-#else /* !_WIN32 */
- zf->data = (unsigned char *) MAP_FAILED;
-#endif /* _WIN32 */
- zf->length = 0;
- zf->numFiles = 0;
- zf->baseOffset = zf->passOffset = 0;
- zf->ptrToFree = NULL;
- zf->passBuf[0] = 0;
-
- /*
- * Actually open the file.
- */
-
- zf->chan = Tcl_OpenFileChannel(interp, zipname, "rb", 0);
- if (!zf->chan) {
- return TCL_ERROR;
- }
-
- /*
- * See if we can get the OS handle. If we can, we can use that to memory
- * map the file, which is nice and efficient. However, it totally depends
- * on the filename pointing to a real regular OS file.
- *
- * Opening real filesystem entities that are not files will lead to an
- * error.
- */
-
- if (Tcl_GetChannelHandle(zf->chan, TCL_READABLE, &handle) == TCL_OK) {
- if (ZipMapArchive(interp, zf, handle) != TCL_OK) {
- goto error;
- }
- } else {
- /*
- * Not an OS file, but rather something in a Tcl VFS. Must copy into
- * memory.
- */
-
- zf->length = Tcl_Seek(zf->chan, 0, SEEK_END);
- if (zf->length == ERROR_LENGTH) {
- ZIPFS_POSIX_ERROR(interp, "seek error");
- goto error;
- }
- /* What's the magic about 64 * 1024 * 1024 ? */
- if ((zf->length <= ZIP_CENTRAL_END_LEN) ||
- (zf->length - ZIP_CENTRAL_END_LEN) >
- (64 * 1024 * 1024 - ZIP_CENTRAL_END_LEN)) {
- ZIPFS_ERROR(interp, "illegal file size");
- ZIPFS_ERROR_CODE(interp, "FILE_SIZE");
- goto error;
- }
- if (Tcl_Seek(zf->chan, 0, SEEK_SET) == -1) {
- ZIPFS_POSIX_ERROR(interp, "seek error");
- goto error;
- }
- zf->ptrToFree = zf->data = (unsigned char *) attemptckalloc(zf->length);
- if (!zf->ptrToFree) {
- ZIPFS_MEM_ERROR(interp);
- goto error;
- }
- i = Tcl_Read(zf->chan, (char *) zf->data, zf->length);
- if (i != zf->length) {
- ZIPFS_POSIX_ERROR(interp, "file read error");
- goto error;
- }
- }
- /*
- * Close the Tcl channel. If the file was mapped, the mapping is
- * unaffected. It is important to close the channel otherwise there is a
- * potential chicken and egg issue at finalization time as the channels
- * are closed before the file systems are dismounted.
- */
- Tcl_Close(interp, zf->chan);
- zf->chan = NULL;
- return ZipFSFindTOC(interp, needZip, zf);
-
- /*
- * Handle errors by closing the archive. This includes closing the channel
- * handle for the archive file.
- */
-
- error:
- ZipFSCloseArchive(interp, zf);
- return TCL_ERROR;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipMapArchive --
- *
- * Wrapper around the platform-specific parts of mmap() (and Windows's
- * equivalent) because it's not part of the standard channel API.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipMapArchive(
- Tcl_Interp *interp, /* Interpreter for error reporting. */
- ZipFile *zf, /* The archive descriptor structure. */
- void *handle) /* The OS handle to the open archive. */
-{
-#ifdef _WIN32
- HANDLE hFile = (HANDLE) handle;
- int readSuccessful;
-
- /*
- * Determine the file size.
- */
-
- readSuccessful = GetFileSizeEx(hFile, (PLARGE_INTEGER) &zf->length) != 0;
- if (!readSuccessful) {
- Tcl_WinConvertError(GetLastError());
- ZIPFS_POSIX_ERROR(interp, "failed to retrieve file size");
- return TCL_ERROR;
- }
- if (zf->length < ZIP_CENTRAL_END_LEN) {
- Tcl_SetErrno(EINVAL);
- ZIPFS_POSIX_ERROR(interp, "truncated file");
- return TCL_ERROR;
- }
- if (zf->length > TCL_SIZE_MAX) {
- Tcl_SetErrno(EFBIG);
- ZIPFS_POSIX_ERROR(interp, "zip archive too big");
- return TCL_ERROR;
- }
-
- /*
- * Map the file.
- */
-
- zf->mountHandle = CreateFileMappingW(hFile, 0, PAGE_READONLY, 0,
- zf->length, 0);
- if (zf->mountHandle == INVALID_HANDLE_VALUE) {
- Tcl_WinConvertError(GetLastError());
- ZIPFS_POSIX_ERROR(interp, "file mapping failed");
- return TCL_ERROR;
- }
- zf->data = (unsigned char *)
- MapViewOfFile(zf->mountHandle, FILE_MAP_READ, 0, 0, zf->length);
- if (!zf->data) {
- Tcl_WinConvertError(GetLastError());
- ZIPFS_POSIX_ERROR(interp, "file mapping failed");
- return TCL_ERROR;
- }
-#else /* !_WIN32 */
- int fd = PTR2INT(handle);
-
- /*
- * Determine the file size.
- */
-
- zf->length = lseek(fd, 0, SEEK_END);
- if (zf->length == ERROR_LENGTH) {
- ZIPFS_POSIX_ERROR(interp, "failed to retrieve file size");
- return TCL_ERROR;
- }
- if (zf->length < ZIP_CENTRAL_END_LEN) {
- Tcl_SetErrno(EINVAL);
- ZIPFS_POSIX_ERROR(interp, "truncated file");
- return TCL_ERROR;
- }
- lseek(fd, 0, SEEK_SET);
-
- zf->data = (unsigned char *)
- mmap(0, zf->length, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
- if (zf->data == MAP_FAILED) {
- ZIPFS_POSIX_ERROR(interp, "file mapping failed");
- return TCL_ERROR;
- }
-#endif /* _WIN32 */
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * IsPasswordValid --
- *
- * Basic test for whether a passowrd is valid. If the test fails, sets an
- * error message in the interpreter.
- *
- * Returns:
- * TCL_OK if the test passes, TCL_ERROR if it fails.
- *
- *-------------------------------------------------------------------------
- */
-
-static inline int
-IsPasswordValid(
- Tcl_Interp *interp,
- const char *passwd,
- size_t pwlen)
-{
- if ((pwlen > 255) || strchr(passwd, 0xff)) {
- ZIPFS_ERROR(interp, "illegal password");
- ZIPFS_ERROR_CODE(interp, "BAD_PASS");
- return TCL_ERROR;
- }
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSCatalogFilesystem --
- *
- * This function generates the root node for a ZIPFS filesystem by
- * reading the ZIP's central directory.
- *
- * Results:
- * TCL_OK on success, TCL_ERROR otherwise with an error message placed
- * into the given "interp" if it is not NULL. On error, frees zf!!
- *
- * Side effects:
- * Will acquire and release the write lock.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSCatalogFilesystem(
- Tcl_Interp *interp, /* Current interpreter. NULLable. */
- ZipFile *zf, /* Temporary buffer hold archive descriptors */
- const char *mountPoint, /* Mount point path. Must be fully normalized */
- const char *passwd, /* Password for opening the ZIP, or NULL if
- * the ZIP is unprotected. */
- const char *zipname) /* Path to ZIP file to build a catalog of. */
-{
- int isNew;
- size_t i, pwlen;
- ZipFile *zf0;
- ZipEntry *z;
- Tcl_HashEntry *hPtr;
- Tcl_DString ds, fpBuf;
- unsigned char *q;
-
- assert(TclIsZipfsPath(mountPoint)); /* Caller should have normalized */
-
- Tcl_DStringInit(&ds);
-
- /*
- * Basic verification of the password for sanity.
- */
-
- pwlen = 0;
- if (passwd) {
- pwlen = strlen(passwd);
- if (IsPasswordValid(interp, passwd, pwlen) != TCL_OK) {
- ZipFSCloseArchive(interp, zf);
- ckfree(zf);
- return TCL_ERROR;
- }
- }
-
- /*
- * Validate the TOC data. If that's bad, things fall apart.
- */
-
- if (zf->baseOffset >= zf->length || zf->passOffset >= zf->length ||
- zf->directoryOffset >= zf->length) {
- ZIPFS_ERROR(interp, "bad zip data");
- ZIPFS_ERROR_CODE(interp, "BAD_ZIP");
- ZipFSCloseArchive(interp, zf);
- ckfree(zf);
- return TCL_ERROR;
- }
-
- WriteLock();
-
- hPtr = Tcl_CreateHashEntry(&ZipFS.zipHash, mountPoint, &isNew);
- if (!isNew) {
- if (interp) {
- zf0 = (ZipFile *) Tcl_GetHashValue(hPtr);
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "%s is already mounted on %s", zf0->name, mountPoint));
- ZIPFS_ERROR_CODE(interp, "MOUNTED");
- }
- Unlock();
- ZipFSCloseArchive(interp, zf);
- Tcl_DStringFree(&ds);
- ckfree(zf);
- return TCL_ERROR;
- }
-
- /*
- * Convert to a real archive descriptor.
- */
-
- zf->mountPoint = (char *) Tcl_GetHashKey(&ZipFS.zipHash, hPtr);
- zf->mountPointLen = strlen(zf->mountPoint);
-
- zf->nameLength = strlen(zipname);
- zf->name = (char *) ckalloc(zf->nameLength + 1);
- memcpy(zf->name, zipname, zf->nameLength + 1);
-
- Tcl_SetHashValue(hPtr, zf);
- if ((zf->passBuf[0] == 0) && pwlen) {
- int k = 0;
-
- zf->passBuf[k++] = pwlen;
- for (i = pwlen; i-- > 0 ;) {
- zf->passBuf[k++] = (passwd[i] & 0x0f)
- | pwrot[(passwd[i] >> 4) & 0x0f];
- }
- zf->passBuf[k] = '\0';
- }
- /* TODO - is this test necessary? When will mountPoint[0] be \0 ? */
- if (mountPoint[0] != '\0') {
- hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, mountPoint, &isNew);
- if (isNew) {
- z = AllocateZipEntry();
- Tcl_SetHashValue(hPtr, z);
-
- z->depth = CountSlashes(mountPoint);
- assert(z->depth >= ZIPFS_ROOTDIR_DEPTH);
- z->zipFilePtr = zf;
- z->isDirectory = (zf->baseOffset == 0) ? 1 : -1; /* root marker */
- z->offset = zf->baseOffset;
- z->compressMethod = ZIP_COMPMETH_STORED;
- z->name = (char *) Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
- if (!strcmp(z->name, ZIPFS_VOLUME)) {
- z->flags |= ZE_F_VOLUME; /* Mark as root volume */
- }
- Tcl_Time t;
- Tcl_GetTime(&t);
- z->timestamp = t.sec;
- z->next = zf->entries;
- zf->entries = z;
- }
- }
- q = zf->data + zf->directoryOffset;
- Tcl_DStringInit(&fpBuf);
- for (i = 0; i < zf->numFiles; i++) {
- const unsigned char *start = zf->data;
- const unsigned char *end = zf->data + zf->length;
- int extra, isdir = 0, dosTime, dosDate, nbcompr;
- size_t offs, pathlen, comlen;
- unsigned char *lq, *gq = NULL;
- char *fullpath, *path;
-
- pathlen = ZipReadShort(start, end, q + ZIP_CENTRAL_PATHLEN_OFFS);
- comlen = ZipReadShort(start, end, q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
- extra = ZipReadShort(start, end, q + ZIP_CENTRAL_EXTRALEN_OFFS);
- Tcl_DStringSetLength(&ds, 0);
- path = DecodeZipEntryText(q + ZIP_CENTRAL_HEADER_LEN, pathlen, &ds);
- if ((pathlen > 0) && (path[pathlen - 1] == '/')) {
- Tcl_DStringSetLength(&ds, pathlen - 1);
- path = Tcl_DStringValue(&ds);
- isdir = 1;
- }
- if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) {
- goto nextent;
- }
- lq = zf->data + zf->baseOffset
- + ZipReadInt(start, end, q + ZIP_CENTRAL_LOCALHDR_OFFS);
- if ((lq < start) || (lq + ZIP_LOCAL_HEADER_LEN > end)) {
- goto nextent;
- }
- nbcompr = ZipReadInt(start, end, lq + ZIP_LOCAL_COMPLEN_OFFS);
- if (!isdir && (nbcompr == 0)
- && (ZipReadInt(start, end, lq + ZIP_LOCAL_UNCOMPLEN_OFFS) == 0)
- && (ZipReadInt(start, end, lq + ZIP_LOCAL_CRC32_OFFS) == 0)) {
- gq = q;
- nbcompr = ZipReadInt(start, end, gq + ZIP_CENTRAL_COMPLEN_OFFS);
- }
- offs = (lq - zf->data)
- + ZIP_LOCAL_HEADER_LEN
- + ZipReadShort(start, end, lq + ZIP_LOCAL_PATHLEN_OFFS)
- + ZipReadShort(start, end, lq + ZIP_LOCAL_EXTRALEN_OFFS);
- if (offs + nbcompr > zf->length) {
- goto nextent;
- }
-
- if (!isdir && (mountPoint[0] == '\0') && !CountSlashes(path)) {
-#ifdef ANDROID
- /*
- * When mounting the ZIP archive on the root directory try to
- * remap top level regular files of the archive to
- * /assets/.root/... since this directory should not be in a valid
- * APK due to the leading dot in the file name component. This
- * trick should make the files AndroidManifest.xml,
- * resources.arsc, and classes.dex visible to Tcl.
- */
- Tcl_DString ds2;
-
- Tcl_DStringInit(&ds2);
- Tcl_DStringAppend(&ds2, "assets/.root/", -1);
- Tcl_DStringAppend(&ds2, path, -1);
- if (ZipFSLookup(Tcl_DStringValue(&ds2))) {
- /* should not happen but skip it anyway */
- Tcl_DStringFree(&ds2);
- goto nextent;
- }
- Tcl_DStringSetLength(&ds, 0);
- Tcl_DStringAppend(&ds, Tcl_DStringValue(&ds2),
- Tcl_DStringLength(&ds2));
- path = Tcl_DStringValue(&ds);
- Tcl_DStringFree(&ds2);
-#else /* !ANDROID */
- /*
- * Regular files skipped when mounting on root.
- */
- goto nextent;
-#endif /* ANDROID */
- }
-
- Tcl_DStringSetLength(&fpBuf, 0);
- fullpath = MapPathToZipfs(interp, mountPoint, path, &fpBuf);
- z = AllocateZipEntry();
- z->depth = CountSlashes(fullpath);
- assert(z->depth >= ZIPFS_ROOTDIR_DEPTH);
- z->zipFilePtr = zf;
- z->isDirectory = isdir;
- z->isEncrypted =
- (ZipReadShort(start, end, lq + ZIP_LOCAL_FLAGS_OFFS) & 1)
- && (nbcompr > ZIP_CRYPT_HDR_LEN);
- z->offset = offs;
- if (gq) {
- z->crc32 = ZipReadInt(start, end, gq + ZIP_CENTRAL_CRC32_OFFS);
- dosDate = ZipReadShort(start, end, gq + ZIP_CENTRAL_MDATE_OFFS);
- dosTime = ZipReadShort(start, end, gq + ZIP_CENTRAL_MTIME_OFFS);
- z->timestamp = DosTimeDate(dosDate, dosTime);
- z->numBytes = ZipReadInt(start, end,
- gq + ZIP_CENTRAL_UNCOMPLEN_OFFS);
- z->compressMethod = ZipReadShort(start, end,
- gq + ZIP_CENTRAL_COMPMETH_OFFS);
- } else {
- z->crc32 = ZipReadInt(start, end, lq + ZIP_LOCAL_CRC32_OFFS);
- dosDate = ZipReadShort(start, end, lq + ZIP_LOCAL_MDATE_OFFS);
- dosTime = ZipReadShort(start, end, lq + ZIP_LOCAL_MTIME_OFFS);
- z->timestamp = DosTimeDate(dosDate, dosTime);
- z->numBytes = ZipReadInt(start, end,
- lq + ZIP_LOCAL_UNCOMPLEN_OFFS);
- z->compressMethod = ZipReadShort(start, end,
- lq + ZIP_LOCAL_COMPMETH_OFFS);
- }
- z->numCompressedBytes = nbcompr;
- hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, fullpath, &isNew);
- if (!isNew) {
- /* should not happen but skip it anyway */
- ckfree(z);
- goto nextent;
- }
-
- Tcl_SetHashValue(hPtr, z);
- z->name = (char *) Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
- z->next = zf->entries;
- zf->entries = z;
- if (isdir && (mountPoint[0] == '\0') && (z->depth == ZIPFS_ROOTDIR_DEPTH)) {
- z->tnext = zf->topEnts;
- zf->topEnts = z;
- }
-
- /*
- * Make any directory nodes we need. ZIPs are not consistent about
- * containing directory nodes.
- */
-
- if (!z->isDirectory && (z->depth > ZIPFS_ROOTDIR_DEPTH)) {
- char *dir, *endPtr;
- ZipEntry *zd;
-
- Tcl_DStringSetLength(&ds, strlen(z->name) + 8);
- Tcl_DStringSetLength(&ds, 0);
- Tcl_DStringAppend(&ds, z->name, -1);
- dir = Tcl_DStringValue(&ds);
- for (endPtr = strrchr(dir, '/'); endPtr && (endPtr != dir);
- endPtr = strrchr(dir, '/')) {
- Tcl_DStringSetLength(&ds, endPtr - dir);
- hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, dir, &isNew);
- if (!isNew) {
- /*
- * Already made. That's fine.
- */
- break;
- }
-
- zd = AllocateZipEntry();
- zd->depth = CountSlashes(dir);
- assert(zd->depth > ZIPFS_ROOTDIR_DEPTH);
- zd->zipFilePtr = zf;
- zd->isDirectory = 1;
- zd->offset = z->offset;
- zd->timestamp = z->timestamp;
- zd->compressMethod = ZIP_COMPMETH_STORED;
- Tcl_SetHashValue(hPtr, zd);
- zd->name = (char *) Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
- zd->next = zf->entries;
- zf->entries = zd;
- if ((mountPoint[0] == '\0') && (zd->depth == ZIPFS_ROOTDIR_DEPTH)) {
- zd->tnext = zf->topEnts;
- zf->topEnts = zd;
- }
- }
- }
- nextent:
- q += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN;
- }
- Unlock();
- Tcl_DStringFree(&fpBuf);
- Tcl_DStringFree(&ds);
- Tcl_FSMountsChanged(NULL);
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipfsSetup --
- *
- * Common initialisation code. ZipFS.initialized must *not* be set prior
- * to the call.
- *
- *-------------------------------------------------------------------------
- */
-
-static void
-ZipfsSetup(void)
-{
-#if TCL_THREADS
- static const Tcl_Time t = { 0, 0 };
-
- /*
- * Inflate condition variable.
- */
-
- Tcl_MutexLock(&ZipFSMutex);
- Tcl_ConditionWait(&ZipFSCond, &ZipFSMutex, &t);
- Tcl_MutexUnlock(&ZipFSMutex);
-#endif /* TCL_THREADS */
-
- crc32tab = get_crc_table();
- Tcl_FSRegister(NULL, &zipfsFilesystem);
- Tcl_InitHashTable(&ZipFS.fileHash, TCL_STRING_KEYS);
- Tcl_InitHashTable(&ZipFS.zipHash, TCL_STRING_KEYS);
- ZipFS.idCount = 1;
- ZipFS.wrmax = DEFAULT_WRITE_MAX_SIZE;
- ZipFS.fallbackEntryEncoding = (char *)
- ckalloc(strlen(ZIPFS_FALLBACK_ENCODING) + 1);
- strcpy(ZipFS.fallbackEntryEncoding, ZIPFS_FALLBACK_ENCODING);
- ZipFS.initialized = 1;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ListMountPoints --
- *
- * This procedure lists the mount points and what's mounted there, or
- * reports whether there are any mounts (if there's no interpreter). The
- * read lock must be held by the caller.
- *
- * Results:
- * A standard Tcl result. TCL_OK (or TCL_BREAK if no mounts and no
- * interpreter).
- *
- * Side effects:
- * Interpreter result may be updated.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ListMountPoints(
- Tcl_Interp *interp)
-{
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch search;
- ZipFile *zf;
- Tcl_Obj *resultList;
-
- if (!interp) {
- /*
- * Are there any entries in the zipHash? Don't need to enumerate them
- * all to know.
- */
-
- return (ZipFS.zipHash.numEntries ? TCL_OK : TCL_BREAK);
- }
-
- TclNewObj(resultList);
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- zf = (ZipFile *) Tcl_GetHashValue(hPtr);
- Tcl_ListObjAppendElement(NULL, resultList, Tcl_NewStringObj(
- zf->mountPoint, -1));
- Tcl_ListObjAppendElement(NULL, resultList, Tcl_NewStringObj(
- zf->name, -1));
- }
- Tcl_SetObjResult(interp, resultList);
- return TCL_OK;
-}
-
-/*
- *------------------------------------------------------------------------
- *
- * CleanupMount --
- *
- * Releases all resources associated with a mounted archive. There
- * must not be any open files in the archive.
- *
- * Caller MUST be holding WriteLock() before calling this function.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Memory associated with the mounted archive is deallocated.
- *------------------------------------------------------------------------
- */
-static void
-CleanupMount(ZipFile *zf) /* Mount point */
-{
- ZipEntry *z, *znext;
- Tcl_HashEntry *hPtr;
- for (z = zf->entries; z; z = znext) {
- znext = z->next;
- hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, z->name);
- if (hPtr) {
- Tcl_DeleteHashEntry(hPtr);
- }
- if (z->data) {
- ckfree(z->data);
- }
- ckfree(z);
- }
- zf->entries = NULL;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * DescribeMounted --
- *
- * This procedure describes what is mounted at the given the mount point.
- * The interpreter result is not updated if there is nothing mounted at
- * the given point. The read lock must be held by the caller.
- *
- * Results:
- * A standard Tcl result. TCL_OK (or TCL_BREAK if nothing mounted there
- * and no interpreter).
- *
- * Side effects:
- * Interpreter result may be updated.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-DescribeMounted(
- Tcl_Interp *interp,
- const char *mountPoint)
-{
- if (interp) {
- ZipFile *zf = ZipFSLookupZip(mountPoint);
-
- if (zf) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(zf->name, -1));
- return TCL_OK;
- }
- }
- return (interp ? TCL_OK : TCL_BREAK);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * TclZipfs_Mount --
- *
- * This procedure is invoked to mount a given ZIP archive file on a given
- * mountpoint with optional ZIP password.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * A ZIP archive file is read, analyzed and mounted, resources are
- * allocated.
- *
- *-------------------------------------------------------------------------
- */
-
-int
-TclZipfs_Mount(
- Tcl_Interp *interp, /* Current interpreter. NULLable. */
- const char *zipname, /* Path to ZIP file to mount */
- const char *mountPoint, /* Mount point path. */
- const char *passwd) /* Password for opening the ZIP, or NULL if
- * the ZIP is unprotected. */
-{
- ZipFile *zf;
- int ret;
-
- ReadLock();
- if (!ZipFS.initialized) {
- ZipfsSetup();
- }
-
- /*
- * No mount point, so list all mount points and what is mounted there.
- */
-
- if (mountPoint == NULL) {
- ret = ListMountPoints(interp);
- Unlock();
- return ret;
- }
-
- Tcl_DString ds;
- Tcl_DStringInit(&ds);
- ret = NormalizeMountPoint(interp, mountPoint, &ds);
- if (ret != TCL_OK) {
- Unlock();
- return ret;
- }
- mountPoint = Tcl_DStringValue(&ds);
-
- if (!zipname) {
- /*
- * Mount point but no file, so describe what is mounted at that mount
- * point.
- */
-
- ret = DescribeMounted(interp, mountPoint);
- Unlock();
- } else {
- /* Have both a mount point and a file (name) to mount there. */
-
- Tcl_Obj *zipPathObj;
- Tcl_Obj *normZipPathObj;
-
- Unlock();
-
- zipPathObj = Tcl_NewStringObj(zipname, -1);
- Tcl_IncrRefCount(zipPathObj);
- normZipPathObj = Tcl_FSGetNormalizedPath(interp, zipPathObj);
- if (normZipPathObj == NULL) {
- Tcl_SetObjResult(
- interp,
- Tcl_ObjPrintf("could not normalize zip filename \"%s\"", zipname));
- Tcl_SetErrorCode(interp, "TCL", "OPERATION", "NORMALIZE", (void *)NULL);
- ret = TCL_ERROR;
- } else {
- Tcl_IncrRefCount(normZipPathObj);
- const char *normPath = TclGetString(normZipPathObj);
- if (passwd == NULL ||
- (ret = IsPasswordValid(interp, passwd, strlen(passwd))) ==
- TCL_OK) {
- zf = AllocateZipFile(interp, strlen(mountPoint));
- if (zf == NULL) {
- ret = TCL_ERROR;
- }
- else {
- ret = ZipFSOpenArchive(interp, normPath, 1, zf);
- if (ret != TCL_OK) {
- ckfree(zf);
- }
- else {
- ret = ZipFSCatalogFilesystem(
- interp, zf, mountPoint, passwd, normPath);
- /* Note zf is already freed on error! */
- }
- }
- }
- Tcl_DecrRefCount(normZipPathObj);
- if (ret == TCL_OK && interp) {
- Tcl_DStringResult(interp, &ds);
- }
- }
- Tcl_DecrRefCount(zipPathObj);
- }
-
- Tcl_DStringFree(&ds);
- return ret;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * TclZipfs_MountBuffer --
- *
- * This procedure is invoked to mount a given ZIP archive file on a given
- * mountpoint.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * A ZIP archive file is read, analyzed and mounted, resources are
- * allocated.
- *
- *-------------------------------------------------------------------------
- */
-
-int
-TclZipfs_MountBuffer(
- Tcl_Interp *interp, /* Current interpreter. NULLable. */
- const void *data,
- size_t datalen,
- const char *mountPoint, /* Mount point path. */
- int copy)
-{
- ZipFile *zf;
- int ret;
-
- if (mountPoint == NULL || data == NULL) {
- ZIPFS_ERROR(interp, "mount point and/or data are null");
- return TCL_ERROR;
- }
-
- /* TODO - how come a *read* lock suffices for initialzing ? */
- ReadLock();
- if (!ZipFS.initialized) {
- ZipfsSetup();
- }
-
- Tcl_DString ds;
- Tcl_DStringInit(&ds);
- ret = NormalizeMountPoint(interp, mountPoint, &ds);
- if (ret != TCL_OK) {
- Unlock();
- return ret;
- }
- mountPoint = Tcl_DStringValue(&ds);
-
- Unlock();
-
- /*
- * Have both a mount point and data to mount there.
- * What's the magic about 64 * 1024 * 1024 ?
- */
- ret = TCL_ERROR;
- if ((datalen <= ZIP_CENTRAL_END_LEN) ||
- (datalen - ZIP_CENTRAL_END_LEN) >
- (64 * 1024 * 1024 - ZIP_CENTRAL_END_LEN)) {
- ZIPFS_ERROR(interp, "illegal file size");
- ZIPFS_ERROR_CODE(interp, "FILE_SIZE");
- goto done;
- }
- zf = AllocateZipFile(interp, strlen(mountPoint));
- if (zf == NULL) {
- goto done;
- }
- zf->isMemBuffer = 1;
- zf->length = datalen;
-
- if (copy) {
- zf->data = (unsigned char *)attemptckalloc(datalen);
- if (zf->data == NULL) {
- ZipFSCloseArchive(interp, zf);
- ckfree(zf);
- ZIPFS_MEM_ERROR(interp);
- goto done;
- }
- memcpy(zf->data, data, datalen);
- zf->ptrToFree = zf->data;
- }
- else {
- zf->data = (unsigned char *)data;
- zf->ptrToFree = NULL;
- }
- ret = ZipFSFindTOC(interp, 1, zf);
- if (ret != TCL_OK) {
- ckfree(zf);
- }
- else {
- /* Note ZipFSCatalogFilesystem will free zf on error */
- ret = ZipFSCatalogFilesystem(
- interp, zf, mountPoint, NULL, "Memory Buffer");
- }
- if (ret == TCL_OK && interp) {
- Tcl_DStringResult(interp, &ds);
- }
-
-done:
- Tcl_DStringFree(&ds);
- return ret;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * TclZipfs_Unmount --
- *
- * This procedure is invoked to unmount a given ZIP archive.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * A mounted ZIP archive file is unmounted, resources are free'd.
- *
- *-------------------------------------------------------------------------
- */
-
-int
-TclZipfs_Unmount(
- Tcl_Interp *interp, /* Current interpreter. NULLable. */
- const char *mountPoint) /* Mount point path. */
-{
- ZipFile *zf;
- Tcl_HashEntry *hPtr;
- Tcl_DString dsm;
- int ret = TCL_OK, unmounted = 0;
-
- Tcl_DStringInit(&dsm);
-
- WriteLock();
- if (!ZipFS.initialized) {
- goto done;
- }
-
- /*
- * Mount point sometimes is a relative or otherwise denormalized path.
- * But an absolute name is needed as mount point here.
- */
-
- if (NormalizeMountPoint(interp, mountPoint, &dsm) != TCL_OK) {
- goto done;
- }
- mountPoint = Tcl_DStringValue(&dsm);
-
- hPtr = Tcl_FindHashEntry(&ZipFS.zipHash, mountPoint);
- /* don't report no-such-mount as an error */
- if (!hPtr) {
- goto done;
- }
-
- zf = (ZipFile *) Tcl_GetHashValue(hPtr);
- if (zf->numOpen > 0) {
- ZIPFS_ERROR(interp, "filesystem is busy");
- ZIPFS_ERROR_CODE(interp, "BUSY");
- ret = TCL_ERROR;
- goto done;
- }
- Tcl_DeleteHashEntry(hPtr);
-
- /*
- * Now no longer mounted - the rest of the code won't find it - but we're
- * still cleaning things up.
- */
-
- CleanupMount(zf);
- ZipFSCloseArchive(interp, zf);
-
- ckfree(zf);
- unmounted = 1;
-
- done:
- Unlock();
- Tcl_DStringFree(&dsm);
- if (unmounted) {
- Tcl_FSMountsChanged(NULL);
- }
- return ret;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSMountObjCmd --
- *
- * This procedure is invoked to process the [zipfs mount] command.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * A ZIP archive file is mounted, resources are allocated.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSMountObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- const char *mountPoint = NULL, *zipFile = NULL, *password = NULL;
- int result;
-
- if (objc > 4) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "?zipfile? ?mountpoint? ?password?");
- return TCL_ERROR;
- }
- /*
- * A single argument is treated as the mountpoint. Two arguments
- * are treated as zipfile and mountpoint.
- */
- if (objc > 1) {
- if (objc == 2) {
- mountPoint = TclGetString(objv[1]);
- } else {
- /* 2 < objc < 4 */
- zipFile = TclGetString(objv[1]);
- mountPoint = TclGetString(objv[2]);
- if (objc > 3) {
- password = TclGetString(objv[3]);
- }
- }
- }
-
- result = TclZipfs_Mount(interp, zipFile, mountPoint, password);
- return result;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSMountBufferObjCmd --
- *
- * This procedure is invoked to process the [zipfs mount_data] command.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * A ZIP archive file is mounted, resources are allocated.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSMountBufferObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- const char *mountPoint = NULL; /* Mount point path. */
- unsigned char *data = NULL;
- Tcl_Size length;
-
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "data mountpoint");
- return TCL_ERROR;
- }
- data = Tcl_GetBytesFromObj(interp, objv[1], &length);
- mountPoint = TclGetString(objv[2]);
- if (data == NULL) {
- return TCL_ERROR;
- }
- return TclZipfs_MountBuffer(interp, data, length, mountPoint, 1);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSRootObjCmd --
- *
- * This procedure is invoked to process the [zipfs root] command. It
- * returns the root that all zipfs file systems are mounted under.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSRootObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc,
- Tcl_Obj *const *objv)
-{
- if (objc != 1) {
- Tcl_WrongNumArgs(interp, 1, objv, "");
- return TCL_ERROR;
- }
- Tcl_SetObjResult(interp, Tcl_NewStringObj(ZIPFS_VOLUME, -1));
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSUnmountObjCmd --
- *
- * This procedure is invoked to process the [zipfs unmount] command.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * A mounted ZIP archive file is unmounted, resources are free'd.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSUnmountObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "mountpoint");
- return TCL_ERROR;
- }
- return TclZipfs_Unmount(interp, TclGetString(objv[1]));
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSMkKeyObjCmd --
- *
- * This procedure is invoked to process the [zipfs mkkey] command. It
- * produces a rotated password to be embedded into an image file.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSMkKeyObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Size len, i = 0;
- const char *pw;
- Tcl_Obj *passObj;
- unsigned char *passBuf;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "password");
- return TCL_ERROR;
- }
- pw = TclGetStringFromObj(objv[1], &len);
- if (len == 0) {
- return TCL_OK;
- }
- if (IsPasswordValid(interp, pw, len) != TCL_OK) {
- return TCL_ERROR;
- }
-
- passObj = Tcl_NewByteArrayObj(NULL, 264);
- passBuf = Tcl_GetByteArrayFromObj(passObj, (Tcl_Size *)NULL);
- while (len > 0) {
- int ch = pw[len - 1];
-
- passBuf[i++] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
- len--;
- }
- passBuf[i] = i;
- i++;
- ZipWriteInt(passBuf, passBuf + 264, passBuf + i, ZIP_PASSWORD_END_SIG);
- Tcl_SetByteArrayLength(passObj, i + 4);
- Tcl_SetObjResult(interp, passObj);
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * RandomChar --
- *
- * Worker for ZipAddFile(). Picks a random character (range: 0..255)
- * using Tcl's standard PRNG.
- *
- * Returns:
- * Tcl result code. Updates chPtr with random character on success.
- *
- * Side effects:
- * Advances the PRNG state. May reenter the Tcl interpreter if the user
- * has replaced the PRNG.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-RandomChar(
- Tcl_Interp *interp,
- int step,
- int *chPtr)
-{
- double r;
- Tcl_Obj *ret;
-
- if (Tcl_EvalEx(interp, "::tcl::mathfunc::rand", TCL_INDEX_NONE, 0) != TCL_OK) {
- goto failed;
- }
- ret = Tcl_GetObjResult(interp);
- if (Tcl_GetDoubleFromObj(interp, ret, &r) != TCL_OK) {
- goto failed;
- }
- *chPtr = (int) (r * 256);
- return TCL_OK;
-
- failed:
- Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
- "\n (evaluating PRNG step %d for password encoding)",
- step));
- return TCL_ERROR;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipAddFile --
- *
- * This procedure is used by ZipFSMkZipOrImg() to add a single file to
- * the output ZIP archive file being written. A ZipEntry struct about the
- * input file is added to the given fileHash table for later creation of
- * the central ZIP directory.
- *
- * Tcl *always* encodes filenames in the ZIP as UTF-8. Similarly, it
- * would always encode comments as UTF-8, if it supported comments.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Input file is read and (compressed and) written to the output ZIP
- * archive file.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipAddFile(
- Tcl_Interp *interp, /* Current interpreter. */
- Tcl_Obj *pathObj, /* Actual name of the file to add. */
- const char *name, /* Name to use in the ZIP archive, in Tcl's
- * internal encoding. */
- Tcl_Channel out, /* The open ZIP archive being built. */
- const char *passwd, /* Password for encoding the file, or NULL if
- * the file is to be unprotected. */
- char *buf, /* Working buffer. */
- int bufsize, /* Size of buf */
- Tcl_HashTable *fileHash) /* Where to record ZIP entry metdata so we can
- * built the central directory. */
-{
- const unsigned char *start = (unsigned char *) buf;
- const unsigned char *end = (unsigned char *) buf + bufsize;
- Tcl_Channel in;
- Tcl_HashEntry *hPtr;
- ZipEntry *z;
- z_stream stream;
- Tcl_DString zpathDs; /* Buffer for the encoded filename. */
- const char *zpathExt; /* Filename in external encoding (true
- * UTF-8). */
- const char *zpathTcl; /* Filename in Tcl's internal encoding. */
- int crc, flush, zpathlen;
- size_t nbyte, nbytecompr, len, olen, align = 0;
- long long headerStartOffset, dataStartOffset, dataEndOffset;
- int mtime = 0, isNew, compMeth;
- unsigned long keys[3], keys0[3];
- char obuf[4096];
-
- /*
- * Trim leading '/' characters. If this results in an empty string, we've
- * nothing to do.
- */
-
- zpathTcl = name;
- while (zpathTcl && zpathTcl[0] == '/') {
- zpathTcl++;
- }
- if (!zpathTcl || (zpathTcl[0] == '\0')) {
- return TCL_OK;
- }
-
- /*
- * Convert to encoded form. Note that we use strlen() here; if someone's
- * crazy enough to embed NULs in filenames, they deserve what they get!
- */
-
- zpathExt = Tcl_UtfToExternalDString(tclUtf8Encoding, zpathTcl, -1, &zpathDs);
- zpathlen = strlen(zpathExt);
- if (zpathlen + ZIP_CENTRAL_HEADER_LEN > bufsize) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "path too long for \"%s\"", TclGetString(pathObj)));
- ZIPFS_ERROR_CODE(interp, "PATH_LEN");
- Tcl_DStringFree(&zpathDs);
- return TCL_ERROR;
- }
- in = Tcl_FSOpenFileChannel(interp, pathObj, "rb", 0);
- if (!in) {
- Tcl_DStringFree(&zpathDs);
-#ifdef _WIN32
- /* hopefully a directory */
- if (strcmp("permission denied", Tcl_PosixError(interp)) == 0) {
- Tcl_Close(interp, in);
- return TCL_OK;
- }
-#endif /* _WIN32 */
- Tcl_Close(interp, in);
- return TCL_ERROR;
- } else {
- Tcl_StatBuf statBuf;
-
- if (Tcl_FSStat(pathObj, &statBuf) != -1) {
- mtime = statBuf.st_mtime;
- }
- }
- Tcl_ResetResult(interp);
-
- /*
- * Compute the CRC.
- */
-
- crc = 0;
- nbyte = nbytecompr = 0;
- while (1) {
- len = Tcl_Read(in, buf, bufsize);
- if (len == ERROR_LENGTH) {
- Tcl_DStringFree(&zpathDs);
- if (nbyte == 0 && errno == EISDIR) {
- Tcl_Close(interp, in);
- return TCL_OK;
- }
- readErrorWithChannelOpen:
- Tcl_SetObjResult(interp, Tcl_ObjPrintf("read error on \"%s\": %s",
- TclGetString(pathObj), Tcl_PosixError(interp)));
- Tcl_Close(interp, in);
- return TCL_ERROR;
- }
- if (len == 0) {
- break;
- }
- crc = crc32(crc, (unsigned char *) buf, len);
- nbyte += len;
- }
- if (Tcl_Seek(in, 0, SEEK_SET) == -1) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf("seek error on \"%s\": %s",
- TclGetString(pathObj), Tcl_PosixError(interp)));
- Tcl_Close(interp, in);
- Tcl_DStringFree(&zpathDs);
- return TCL_ERROR;
- }
-
- /*
- * Remember where we've got to so far so we can write the header (after
- * writing the file).
- */
-
- headerStartOffset = Tcl_Tell(out);
-
- /*
- * Reserve space for the per-file header. Includes writing the file name
- * as we already know that.
- */
-
- memset(buf, '\0', ZIP_LOCAL_HEADER_LEN);
- memcpy(buf + ZIP_LOCAL_HEADER_LEN, zpathExt, zpathlen);
- len = zpathlen + ZIP_LOCAL_HEADER_LEN;
- if ((size_t) Tcl_Write(out, buf, len) != len) {
- writeErrorWithChannelOpen:
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error on \"%s\": %s",
- TclGetString(pathObj), Tcl_PosixError(interp)));
- Tcl_Close(interp, in);
- Tcl_DStringFree(&zpathDs);
- return TCL_ERROR;
- }
-
- /*
- * Align payload to next 4-byte boundary (if necessary) using a dummy
- * extra entry similar to the zipalign tool from Android's SDK.
- */
-
- if ((len + headerStartOffset) & 3) {
- unsigned char abuf[8];
- const unsigned char *astart = abuf;
- const unsigned char *aend = abuf + 8;
-
- align = 4 + ((len + headerStartOffset) & 3);
- ZipWriteShort(astart, aend, abuf, 0xffff);
- ZipWriteShort(astart, aend, abuf + 2, align - 4);
- ZipWriteInt(astart, aend, abuf + 4, 0x03020100);
- if ((size_t) Tcl_Write(out, (const char *) abuf, align) != align) {
- goto writeErrorWithChannelOpen;
- }
- }
-
- /*
- * Set up encryption if we were asked to.
- */
-
- if (passwd) {
- int i, ch, tmp;
- unsigned char kvbuf[2*ZIP_CRYPT_HDR_LEN];
-
- init_keys(passwd, keys, crc32tab);
- for (i = 0; i < ZIP_CRYPT_HDR_LEN - 2; i++) {
- if (RandomChar(interp, i, &ch) != TCL_OK) {
- Tcl_Close(interp, in);
- return TCL_ERROR;
- }
- kvbuf[i + ZIP_CRYPT_HDR_LEN] = UCHAR(zencode(keys, crc32tab, ch, tmp));
- }
- Tcl_ResetResult(interp);
- init_keys(passwd, keys, crc32tab);
- for (i = 0; i < ZIP_CRYPT_HDR_LEN - 2; i++) {
- kvbuf[i] = UCHAR(zencode(keys, crc32tab, kvbuf[i + ZIP_CRYPT_HDR_LEN], tmp));
- }
- kvbuf[i++] = UCHAR(zencode(keys, crc32tab, crc >> 16, tmp));
- kvbuf[i++] = UCHAR(zencode(keys, crc32tab, crc >> 24, tmp));
- len = Tcl_Write(out, (char *) kvbuf, ZIP_CRYPT_HDR_LEN);
- memset(kvbuf, 0, sizeof(kvbuf));
- if (len != ZIP_CRYPT_HDR_LEN) {
- goto writeErrorWithChannelOpen;
- }
- memcpy(keys0, keys, sizeof(keys0));
- nbytecompr += ZIP_CRYPT_HDR_LEN;
- }
-
- /*
- * Save where we've got to in case we need to just store this file.
- */
-
- Tcl_Flush(out);
- dataStartOffset = Tcl_Tell(out);
-
- /*
- * Compress the stream.
- */
-
- compMeth = ZIP_COMPMETH_DEFLATED;
- memset(&stream, 0, sizeof(z_stream));
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
- if (deflateInit2(&stream, 9, Z_DEFLATED, -15, 8,
- Z_DEFAULT_STRATEGY) != Z_OK) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "compression init error on \"%s\"", TclGetString(pathObj)));
- ZIPFS_ERROR_CODE(interp, "DEFLATE_INIT");
- Tcl_Close(interp, in);
- Tcl_DStringFree(&zpathDs);
- return TCL_ERROR;
- }
-
- do {
- len = Tcl_Read(in, buf, bufsize);
- if (len == ERROR_LENGTH) {
- deflateEnd(&stream);
- goto readErrorWithChannelOpen;
- }
- stream.avail_in = len;
- stream.next_in = (unsigned char *) buf;
- flush = Tcl_Eof(in) ? Z_FINISH : Z_NO_FLUSH;
- do {
- stream.avail_out = sizeof(obuf);
- stream.next_out = (unsigned char *) obuf;
- len = deflate(&stream, flush);
- if (len == (size_t) Z_STREAM_ERROR) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "deflate error on \"%s\"", TclGetString(pathObj)));
- ZIPFS_ERROR_CODE(interp, "DEFLATE");
- deflateEnd(&stream);
- Tcl_Close(interp, in);
- Tcl_DStringFree(&zpathDs);
- return TCL_ERROR;
- }
- olen = sizeof(obuf) - stream.avail_out;
- if (passwd) {
- size_t i;
- int tmp;
-
- for (i = 0; i < olen; i++) {
- obuf[i] = (char) zencode(keys, crc32tab, obuf[i], tmp);
- }
- }
- if (olen && ((size_t) Tcl_Write(out, obuf, olen) != olen)) {
- deflateEnd(&stream);
- goto writeErrorWithChannelOpen;
- }
- nbytecompr += olen;
- } while (stream.avail_out == 0);
- } while (flush != Z_FINISH);
- deflateEnd(&stream);
-
- /*
- * Work out where we've got to.
- */
-
- Tcl_Flush(out);
- dataEndOffset = Tcl_Tell(out);
-
- if (nbyte - nbytecompr <= 0) {
- /*
- * Compressed file larger than input, write it again uncompressed.
- */
-
- if (Tcl_Seek(in, 0, SEEK_SET) != 0) {
- goto seekErr;
- }
- if (Tcl_Seek(out, dataStartOffset, SEEK_SET) != dataStartOffset) {
- seekErr:
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "seek error: %s", Tcl_PosixError(interp)));
- Tcl_Close(interp, in);
- Tcl_DStringFree(&zpathDs);
- return TCL_ERROR;
- }
- nbytecompr = (passwd ? ZIP_CRYPT_HDR_LEN : 0);
- while (1) {
- len = Tcl_Read(in, buf, bufsize);
- if (len == ERROR_LENGTH) {
- goto readErrorWithChannelOpen;
- } else if (len == 0) {
- break;
- }
- if (passwd) {
- size_t i;
- int tmp;
-
- for (i = 0; i < len; i++) {
- buf[i] = (char) zencode(keys0, crc32tab, buf[i], tmp);
- }
- }
- if ((size_t) Tcl_Write(out, buf, len) != len) {
- goto writeErrorWithChannelOpen;
- }
- nbytecompr += len;
- }
- compMeth = ZIP_COMPMETH_STORED;
-
- /*
- * Chop off everything after this; it's the over-large compressed data
- * and we don't know if it is going to get overwritten otherwise.
- */
-
- Tcl_Flush(out);
- dataEndOffset = Tcl_Tell(out);
- Tcl_TruncateChannel(out, dataEndOffset);
- }
- Tcl_Close(interp, in);
- Tcl_DStringFree(&zpathDs);
- zpathExt = NULL;
-
- hPtr = Tcl_CreateHashEntry(fileHash, zpathTcl, &isNew);
- if (!isNew) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "non-unique path name \"%s\"", TclGetString(pathObj)));
- ZIPFS_ERROR_CODE(interp, "DUPLICATE_PATH");
- return TCL_ERROR;
- }
-
- /*
- * Remember that we've written the file (for central directory generation)
- * and generate the local (per-file) header in the space that we reserved
- * earlier.
- */
-
- z = AllocateZipEntry();
- Tcl_SetHashValue(hPtr, z);
- z->isEncrypted = (passwd ? 1 : 0);
- z->offset = headerStartOffset;
- z->crc32 = crc;
- z->timestamp = mtime;
- z->numBytes = nbyte;
- z->numCompressedBytes = nbytecompr;
- z->compressMethod = compMeth;
- z->name = (char *) Tcl_GetHashKey(fileHash, hPtr);
-
- /*
- * Write final local header information.
- */
-
- SerializeLocalEntryHeader(start, end, (unsigned char *) buf, z,
- zpathlen, align);
- if (Tcl_Seek(out, headerStartOffset, SEEK_SET) != headerStartOffset) {
- Tcl_DeleteHashEntry(hPtr);
- ckfree(z);
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "seek error: %s", Tcl_PosixError(interp)));
- return TCL_ERROR;
- }
- if (Tcl_Write(out, buf, ZIP_LOCAL_HEADER_LEN) != ZIP_LOCAL_HEADER_LEN) {
- Tcl_DeleteHashEntry(hPtr);
- ckfree(z);
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error: %s", Tcl_PosixError(interp)));
- return TCL_ERROR;
- }
- Tcl_Flush(out);
- if (Tcl_Seek(out, dataEndOffset, SEEK_SET) != dataEndOffset) {
- Tcl_DeleteHashEntry(hPtr);
- ckfree(z);
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "seek error: %s", Tcl_PosixError(interp)));
- return TCL_ERROR;
- }
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSFind --
- *
- * Worker for ZipFSMkZipOrImg() that discovers the list of files to add.
- * Simple wrapper around [zipfs find].
- *
- *-------------------------------------------------------------------------
- */
-
-static Tcl_Obj *
-ZipFSFind(
- Tcl_Interp *interp,
- Tcl_Obj *dirRoot)
-{
- Tcl_Obj *cmd[2];
- int result;
-
- cmd[0] = Tcl_NewStringObj("::tcl::zipfs::find", -1);
- cmd[1] = dirRoot;
- Tcl_IncrRefCount(cmd[0]);
- result = Tcl_EvalObjv(interp, 2, cmd, 0);
- Tcl_DecrRefCount(cmd[0]);
- if (result != TCL_OK) {
- return NULL;
- }
- return Tcl_GetObjResult(interp);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ComputeNameInArchive --
- *
- * Helper for ZipFSMkZipOrImg() that computes what the actual name of a
- * file in the ZIP archive should be, stripping a prefix (if appropriate)
- * and any leading slashes. If the result is an empty string, the entry
- * should be skipped.
- *
- * Returns:
- * Pointer to the name (in Tcl's internal encoding), which will be in
- * memory owned by one of the argument objects.
- *
- * Side effects:
- * None (if Tcl_Objs have string representations)
- *
- *-------------------------------------------------------------------------
- */
-
-static inline const char *
-ComputeNameInArchive(
- Tcl_Obj *pathObj, /* The path to the origin file */
- Tcl_Obj *directNameObj, /* User-specified name for use in the ZIP
- * archive */
- const char *strip, /* A prefix to strip; may be NULL if no
- * stripping need be done. */
- Tcl_Size slen) /* The length of the prefix; must be 0 if no
- * stripping need be done. */
-{
- const char *name;
- Tcl_Size len;
-
- if (directNameObj) {
- name = TclGetString(directNameObj);
- } else {
- name = TclGetStringFromObj(pathObj, &len);
- if (slen > 0) {
- if ((len <= slen) || (strncmp(strip, name, slen) != 0)) {
- /*
- * Guaranteed to be a NUL at the end, which will make this
- * entry be skipped.
- */
-
- return name + len;
- }
- name += slen;
- }
- }
- while (name[0] == '/') {
- ++name;
- }
- return name;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSMkZipOrImg --
- *
- * This procedure is creates a new ZIP archive file or image file given
- * output filename, input directory of files to be archived, optional
- * password, and optional image to be prepended to the output ZIP archive
- * file. It's the core of the implementation of [zipfs mkzip], [zipfs
- * mkimg], [zipfs lmkzip] and [zipfs lmkimg].
- *
- * Tcl *always* encodes filenames in the ZIP as UTF-8. Similarly, it
- * would always encode comments as UTF-8, if it supported comments.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * A new ZIP archive file or image file is written.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSMkZipOrImg(
- Tcl_Interp *interp, /* Current interpreter. */
- int isImg, /* Are we making an image? */
- Tcl_Obj *targetFile, /* What file are we making? */
- Tcl_Obj *dirRoot, /* What directory do we take files from? Do
- * not specify at the same time as
- * mappingList (one must be NULL). */
- Tcl_Obj *mappingList, /* What files are we putting in, and with what
- * names? Do not specify at the same time as
- * dirRoot (one must be NULL). */
- Tcl_Obj *originFile, /* If we're making an image, what file does
- * the non-ZIP part of the image come from? */
- Tcl_Obj *stripPrefix, /* Are we going to strip a prefix from
- * filenames found beneath dirRoot? If NULL,
- * do not strip anything (except for dirRoot
- * itself). */
- Tcl_Obj *passwordObj) /* The password for encoding things. NULL if
- * there's no password protection. */
-{
- Tcl_Channel out;
- int count, ret = TCL_ERROR;
- int pwlen = 0, slen = 0, lobjc;
- size_t len, i = 0;
- long long directoryStartOffset;
- /* The overall file offset of the start of the
- * central directory. */
- long long suffixStartOffset;/* The overall file offset of the start of the
- * suffix of the central directory (i.e.,
- * where this data will be written). */
- Tcl_Obj **lobjv, *list = mappingList;
- ZipEntry *z;
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch search;
- Tcl_HashTable fileHash;
- char *strip = NULL, *pw = NULL, passBuf[264], buf[4096];
- unsigned char *start = (unsigned char *) buf;
- unsigned char *end = start + sizeof(buf);
-
- /*
- * Caller has verified that the number of arguments is correct.
- */
-
- passBuf[0] = 0;
- if (passwordObj != NULL) {
- pw = TclGetStringFromObj(passwordObj, &pwlen);
- if (IsPasswordValid(interp, pw, pwlen) != TCL_OK) {
- return TCL_ERROR;
- }
- if (pwlen <= 0) {
- pw = NULL;
- pwlen = 0;
- }
- }
- if (dirRoot != NULL) {
- list = ZipFSFind(interp, dirRoot);
- if (!list) {
- return TCL_ERROR;
- }
- }
- Tcl_IncrRefCount(list);
- if (TclListObjLength(interp, list, &lobjc) != TCL_OK) {
- Tcl_DecrRefCount(list);
- return TCL_ERROR;
- }
- if (mappingList && (lobjc % 2)) {
- Tcl_DecrRefCount(list);
- ZIPFS_ERROR(interp, "need even number of elements");
- ZIPFS_ERROR_CODE(interp, "LIST_LENGTH");
- return TCL_ERROR;
- }
- if (lobjc == 0) {
- Tcl_DecrRefCount(list);
- ZIPFS_ERROR(interp, "empty archive");
- ZIPFS_ERROR_CODE(interp, "EMPTY");
- return TCL_ERROR;
- }
- if (TclListObjGetElements(interp, list, &lobjc, &lobjv) != TCL_OK) {
- Tcl_DecrRefCount(list);
- return TCL_ERROR;
- }
- out = Tcl_FSOpenFileChannel(interp, targetFile, "wb", 0755);
- if (out == NULL) {
- Tcl_DecrRefCount(list);
- return TCL_ERROR;
- }
-
- /*
- * Copy the existing contents from the image if it is an executable image.
- * Care must be taken because this might include an existing ZIP, which
- * needs to be stripped.
- */
-
- if (isImg) {
- ZipFile *zf, zf0;
- int isMounted = 0;
- const char *imgName;
-
- // TODO: normalize the origin file name
- imgName = (originFile != NULL) ? TclGetString(originFile) :
- Tcl_GetNameOfExecutable();
- if (pwlen) {
- i = 0;
- for (len = pwlen; len-- > 0;) {
- int ch = pw[len];
-
- passBuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
- i++;
- }
- passBuf[i] = i;
- ++i;
- passBuf[i++] = (char) ZIP_PASSWORD_END_SIG;
- passBuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 8);
- passBuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 16);
- passBuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 24);
- passBuf[i] = '\0';
- }
-
- /*
- * Check for mounted image.
- */
-
- WriteLock();
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- zf = (ZipFile *) Tcl_GetHashValue(hPtr);
- if (strcmp(zf->name, imgName) == 0) {
- isMounted = 1;
- zf->numOpen++;
- break;
- }
- }
- Unlock();
-
- if (!isMounted) {
- zf = &zf0;
- memset(&zf0, 0, sizeof(ZipFile));
- }
- if (isMounted || ZipFSOpenArchive(interp, imgName, 0, zf) == TCL_OK) {
- /*
- * Copy everything up to the ZIP-related suffix.
- */
-
- if ((size_t) Tcl_Write(out, (char *) zf->data,
- zf->passOffset) != zf->passOffset) {
- memset(passBuf, 0, sizeof(passBuf));
- Tcl_DecrRefCount(list);
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error: %s", Tcl_PosixError(interp)));
- Tcl_Close(interp, out);
- if (zf == &zf0) {
- ZipFSCloseArchive(interp, zf);
- } else {
- WriteLock();
- zf->numOpen--;
- Unlock();
- }
- return TCL_ERROR;
- }
- if (zf == &zf0) {
- ZipFSCloseArchive(interp, zf);
- } else {
- WriteLock();
- zf->numOpen--;
- Unlock();
- }
- } else {
- /*
- * Fall back to read it as plain file which hopefully is a static
- * tclsh or wish binary with proper zipfs infrastructure built in.
- */
-
- if (CopyImageFile(interp, imgName, out) != TCL_OK) {
- memset(passBuf, 0, sizeof(passBuf));
- Tcl_DecrRefCount(list);
- Tcl_Close(interp, out);
- return TCL_ERROR;
- }
- }
-
- /*
- * Store the password so that the automounter can find it.
- */
-
- len = strlen(passBuf);
- if (len > 0) {
- i = Tcl_Write(out, passBuf, len);
- if (i != len) {
- Tcl_DecrRefCount(list);
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error: %s", Tcl_PosixError(interp)));
- Tcl_Close(interp, out);
- return TCL_ERROR;
- }
- }
- memset(passBuf, 0, sizeof(passBuf));
- Tcl_Flush(out);
- }
-
- /*
- * Prepare the contents of the ZIP archive.
- */
-
- Tcl_InitHashTable(&fileHash, TCL_STRING_KEYS);
- if (mappingList == NULL && stripPrefix != NULL) {
- strip = TclGetStringFromObj(stripPrefix, &slen);
- if (!slen) {
- strip = NULL;
- }
- }
- for (i = 0; i < (size_t) lobjc; i += (mappingList ? 2 : 1)) {
- Tcl_Obj *pathObj = lobjv[i];
- const char *name = ComputeNameInArchive(pathObj,
- (mappingList ? lobjv[i + 1] : NULL), strip, slen);
-
- if (name[0] == '\0') {
- continue;
- }
- if (ZipAddFile(interp, pathObj, name, out, pw, buf, sizeof(buf),
- &fileHash) != TCL_OK) {
- goto done;
- }
- }
-
- /*
- * Construct the contents of the ZIP central directory.
- */
-
- directoryStartOffset = Tcl_Tell(out);
- count = 0;
- for (i = 0; i < (size_t) lobjc; i += (mappingList ? 2 : 1)) {
- const char *name = ComputeNameInArchive(lobjv[i],
- (mappingList ? lobjv[i + 1] : NULL), strip, slen);
- Tcl_DString ds;
-
- hPtr = Tcl_FindHashEntry(&fileHash, name);
- if (!hPtr) {
- continue;
- }
- z = (ZipEntry *) Tcl_GetHashValue(hPtr);
-
- name = Tcl_UtfToExternalDString(tclUtf8Encoding, z->name, TCL_INDEX_NONE, &ds);
- len = Tcl_DStringLength(&ds);
- SerializeCentralDirectoryEntry(start, end, (unsigned char *) buf,
- z, len);
- if ((Tcl_Write(out, buf, ZIP_CENTRAL_HEADER_LEN)
- != ZIP_CENTRAL_HEADER_LEN)
- || ((size_t) Tcl_Write(out, name, len) != len)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error: %s", Tcl_PosixError(interp)));
- Tcl_DStringFree(&ds);
- goto done;
- }
- Tcl_DStringFree(&ds);
- count++;
- }
-
- /*
- * Finalize the central directory.
- */
-
- Tcl_Flush(out);
- suffixStartOffset = Tcl_Tell(out);
- SerializeCentralDirectorySuffix(start, end, (unsigned char *) buf,
- count, directoryStartOffset, suffixStartOffset);
- if (Tcl_Write(out, buf, ZIP_CENTRAL_END_LEN) != ZIP_CENTRAL_END_LEN) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error: %s", Tcl_PosixError(interp)));
- goto done;
- }
- Tcl_Flush(out);
- ret = TCL_OK;
-
- done:
- if (ret == TCL_OK) {
- ret = Tcl_Close(interp, out);
- } else {
- Tcl_Close(interp, out);
- }
- Tcl_DecrRefCount(list);
- for (hPtr = Tcl_FirstHashEntry(&fileHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- z = (ZipEntry *) Tcl_GetHashValue(hPtr);
- ckfree(z);
- Tcl_DeleteHashEntry(hPtr);
- }
- Tcl_DeleteHashTable(&fileHash);
- return ret;
-}
-
-/*
- * ---------------------------------------------------------------------
- *
- * CopyImageFile --
- *
- * A simple file copy function that is used (by ZipFSMkZipOrImg) for
- * anything that is not an image with a ZIP appended.
- *
- * Returns:
- * A Tcl result code.
- *
- * Side effects:
- * Writes to an output channel.
- *
- * ---------------------------------------------------------------------
- */
-
-static int
-CopyImageFile(
- Tcl_Interp *interp, /* For error reporting. */
- const char *imgName, /* Where to copy from. */
- Tcl_Channel out) /* Where to copy to; already open for writing
- * binary data. */
-{
- size_t i, k;
- Tcl_Size m, n;
- Tcl_Channel in;
- char buf[4096];
- const char *errMsg;
-
- Tcl_ResetResult(interp);
- in = Tcl_OpenFileChannel(interp, imgName, "rb", 0644);
- if (!in) {
- return TCL_ERROR;
- }
-
- /*
- * Get the length of the file (and exclude non-files).
- */
-
- i = Tcl_Seek(in, 0, SEEK_END);
- if (i == ERROR_LENGTH) {
- errMsg = "seek error";
- goto copyError;
- }
- Tcl_Seek(in, 0, SEEK_SET);
-
- /*
- * Copy the whole file, 8 blocks at a time (reasonably efficient). Note
- * that this totally ignores things like Windows's Alternate File Streams.
- */
-
- for (k = 0; k < i; k += m) {
- m = i - k;
- if (m > (Tcl_Size) sizeof(buf)) {
- m = sizeof(buf);
- }
- n = Tcl_Read(in, buf, m);
- if (n == -1) {
- errMsg = "read error";
- goto copyError;
- } else if (n == 0) {
- break;
- }
- m = Tcl_Write(out, buf, n);
- if (m != n) {
- errMsg = "write error";
- goto copyError;
- }
- }
- Tcl_Close(interp, in);
- return TCL_OK;
-
- copyError:
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "%s: %s", errMsg, Tcl_PosixError(interp)));
- Tcl_Close(interp, in);
- return TCL_ERROR;
-}
-
-/*
- * ---------------------------------------------------------------------
- *
- * SerializeLocalEntryHeader, SerializeCentralDirectoryEntry,
- * SerializeCentralDirectorySuffix --
- *
- * Create serialized forms of the structures that make up the ZIP
- * metadata. Note that the both the local entry and the central directory
- * entry need to have the name of the entry written directly afterwards.
- *
- * We could write these as structs except we need to guarantee that we
- * are writing these out as little-endian values.
- *
- * Side effects:
- * Both update their buffer arguments, but otherwise change nothing.
- *
- * ---------------------------------------------------------------------
- */
-
-static void
-SerializeLocalEntryHeader(
- const unsigned char *start, /* The start of writable memory. */
- const unsigned char *end, /* The end of writable memory. */
- unsigned char *buf, /* Where to serialize to */
- ZipEntry *z, /* The description of what to serialize. */
- int nameLength, /* The length of the name. */
- int align) /* The number of alignment bytes. */
-{
- ZipWriteInt(start, end, buf + ZIP_LOCAL_SIG_OFFS, ZIP_LOCAL_HEADER_SIG);
- ZipWriteShort(start, end, buf + ZIP_LOCAL_VERSION_OFFS, ZIP_MIN_VERSION);
- ZipWriteShort(start, end, buf + ZIP_LOCAL_FLAGS_OFFS, z->isEncrypted);
- ZipWriteShort(start, end, buf + ZIP_LOCAL_COMPMETH_OFFS,
- z->compressMethod);
- ZipWriteShort(start, end, buf + ZIP_LOCAL_MTIME_OFFS,
- ToDosTime(z->timestamp));
- ZipWriteShort(start, end, buf + ZIP_LOCAL_MDATE_OFFS,
- ToDosDate(z->timestamp));
- ZipWriteInt(start, end, buf + ZIP_LOCAL_CRC32_OFFS, z->crc32);
- ZipWriteInt(start, end, buf + ZIP_LOCAL_COMPLEN_OFFS,
- z->numCompressedBytes);
- ZipWriteInt(start, end, buf + ZIP_LOCAL_UNCOMPLEN_OFFS, z->numBytes);
- ZipWriteShort(start, end, buf + ZIP_LOCAL_PATHLEN_OFFS, nameLength);
- ZipWriteShort(start, end, buf + ZIP_LOCAL_EXTRALEN_OFFS, align);
-}
-
-static void
-SerializeCentralDirectoryEntry(
- const unsigned char *start, /* The start of writable memory. */
- const unsigned char *end, /* The end of writable memory. */
- unsigned char *buf, /* Where to serialize to */
- ZipEntry *z, /* The description of what to serialize. */
- size_t nameLength) /* The length of the name. */
-{
- ZipWriteInt(start, end, buf + ZIP_CENTRAL_SIG_OFFS,
- ZIP_CENTRAL_HEADER_SIG);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_VERSIONMADE_OFFS,
- ZIP_MIN_VERSION);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_VERSION_OFFS, ZIP_MIN_VERSION);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_FLAGS_OFFS, z->isEncrypted);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_COMPMETH_OFFS,
- z->compressMethod);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_MTIME_OFFS,
- ToDosTime(z->timestamp));
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_MDATE_OFFS,
- ToDosDate(z->timestamp));
- ZipWriteInt(start, end, buf + ZIP_CENTRAL_CRC32_OFFS, z->crc32);
- ZipWriteInt(start, end, buf + ZIP_CENTRAL_COMPLEN_OFFS,
- z->numCompressedBytes);
- ZipWriteInt(start, end, buf + ZIP_CENTRAL_UNCOMPLEN_OFFS, z->numBytes);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_PATHLEN_OFFS, nameLength);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_EXTRALEN_OFFS, 0);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_FCOMMENTLEN_OFFS, 0);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_DISKFILE_OFFS, 0);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_IATTR_OFFS, 0);
- ZipWriteInt(start, end, buf + ZIP_CENTRAL_EATTR_OFFS, 0);
- ZipWriteInt(start, end, buf + ZIP_CENTRAL_LOCALHDR_OFFS,
- z->offset);
-}
-
-static void
-SerializeCentralDirectorySuffix(
- const unsigned char *start, /* The start of writable memory. */
- const unsigned char *end, /* The end of writable memory. */
- unsigned char *buf, /* Where to serialize to */
- int entryCount, /* The number of entries in the directory */
- long long directoryStartOffset,
- /* The overall file offset of the start of the
- * central directory. */
- long long suffixStartOffset)/* The overall file offset of the start of the
- * suffix of the central directory (i.e.,
- * where this data will be written). */
-{
- ZipWriteInt(start, end, buf + ZIP_CENTRAL_END_SIG_OFFS,
- ZIP_CENTRAL_END_SIG);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_DISKNO_OFFS, 0);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_DISKDIR_OFFS, 0);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_ENTS_OFFS, entryCount);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_TOTALENTS_OFFS, entryCount);
- ZipWriteInt(start, end, buf + ZIP_CENTRAL_DIRSIZE_OFFS,
- suffixStartOffset - directoryStartOffset);
- ZipWriteInt(start, end, buf + ZIP_CENTRAL_DIRSTART_OFFS,
- directoryStartOffset);
- ZipWriteShort(start, end, buf + ZIP_CENTRAL_COMMENTLEN_OFFS, 0);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSMkZipObjCmd, ZipFSLMkZipObjCmd --
- *
- * These procedures are invoked to process the [zipfs mkzip] and [zipfs
- * lmkzip] commands. See description of ZipFSMkZipOrImg().
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See description of ZipFSMkZipOrImg().
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSMkZipObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Obj *stripPrefix, *password;
-
- if (objc < 3 || objc > 5) {
- Tcl_WrongNumArgs(interp, 1, objv, "outfile indir ?strip? ?password?");
- return TCL_ERROR;
- }
- if (Tcl_IsSafe(interp)) {
- ZIPFS_ERROR(interp, "operation not permitted in a safe interpreter");
- ZIPFS_ERROR_CODE(interp, "SAFE_INTERP");
- return TCL_ERROR;
- }
-
- stripPrefix = (objc > 3 ? objv[3] : NULL);
- password = (objc > 4 ? objv[4] : NULL);
- return ZipFSMkZipOrImg(interp, 0, objv[1], objv[2], NULL, NULL,
- stripPrefix, password);
-}
-
-static int
-ZipFSLMkZipObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Obj *password;
-
- if (objc < 3 || objc > 4) {
- Tcl_WrongNumArgs(interp, 1, objv, "outfile inlist ?password?");
- return TCL_ERROR;
- }
- if (Tcl_IsSafe(interp)) {
- ZIPFS_ERROR(interp, "operation not permitted in a safe interpreter");
- ZIPFS_ERROR_CODE(interp, "SAFE_INTERP");
- return TCL_ERROR;
- }
-
- password = (objc > 3 ? objv[3] : NULL);
- return ZipFSMkZipOrImg(interp, 0, objv[1], NULL, objv[2], NULL,
- NULL, password);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSMkImgObjCmd, ZipFSLMkImgObjCmd --
- *
- * These procedures are invoked to process the [zipfs mkimg] and [zipfs
- * lmkimg] commands. See description of ZipFSMkZipOrImg().
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See description of ZipFSMkZipOrImg().
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSMkImgObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Obj *originFile, *stripPrefix, *password;
-
- if (objc < 3 || objc > 6) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "outfile indir ?strip? ?password? ?infile?");
- return TCL_ERROR;
- }
- if (Tcl_IsSafe(interp)) {
- ZIPFS_ERROR(interp, "operation not permitted in a safe interpreter");
- ZIPFS_ERROR_CODE(interp, "SAFE_INTERP");
- return TCL_ERROR;
- }
-
- originFile = (objc > 5 ? objv[5] : NULL);
- stripPrefix = (objc > 3 ? objv[3] : NULL);
- password = (objc > 4 ? objv[4] : NULL);
- return ZipFSMkZipOrImg(interp, 1, objv[1], objv[2], NULL,
- originFile, stripPrefix, password);
-}
-
-static int
-ZipFSLMkImgObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- Tcl_Obj *originFile, *password;
-
- if (objc < 3 || objc > 5) {
- Tcl_WrongNumArgs(interp, 1, objv, "outfile inlist ?password? ?infile?");
- return TCL_ERROR;
- }
- if (Tcl_IsSafe(interp)) {
- ZIPFS_ERROR(interp, "operation not permitted in a safe interpreter");
- ZIPFS_ERROR_CODE(interp, "SAFE_INTERP");
- return TCL_ERROR;
- }
-
- originFile = (objc > 4 ? objv[4] : NULL);
- password = (objc > 3 ? objv[3] : NULL);
- return ZipFSMkZipOrImg(interp, 1, objv[1], NULL, objv[2],
- originFile, NULL, password);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSCanonicalObjCmd --
- *
- * This procedure is invoked to process the [zipfs canonical] command.
- * It returns the canonical name for a file within zipfs
- *
- * Results:
- * Always TCL_OK provided the right number of arguments are supplied.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSCanonicalObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- const char *mntPoint = NULL;
- Tcl_DString dsPath, dsMount;
-
- if (objc < 2 || objc > 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "?mountpoint? filename");
- return TCL_ERROR;
- }
-
- Tcl_DStringInit(&dsPath);
- Tcl_DStringInit(&dsMount);
-
- if (objc == 2) {
- mntPoint = ZIPFS_VOLUME;
- } else {
- if (NormalizeMountPoint(interp, TclGetString(objv[1]), &dsMount) != TCL_OK) {
- return TCL_ERROR;
- }
- mntPoint = Tcl_DStringValue(&dsMount);
- }
- (void)MapPathToZipfs(interp,
- mntPoint,
- TclGetString(objv[objc - 1]),
- &dsPath);
- Tcl_SetObjResult(interp, Tcl_DStringToObj(&dsPath));
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSExistsObjCmd --
- *
- * This procedure is invoked to process the [zipfs exists] command. It
- * tests for the existence of a file in the ZIP filesystem and places a
- * boolean into the interp's result.
- *
- * Results:
- * Always TCL_OK provided the right number of arguments are supplied.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSExistsObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- char *filename;
- int exists;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "filename");
- return TCL_ERROR;
- }
-
- filename = TclGetString(objv[1]);
-
- ReadLock();
- exists = ZipFSLookup(filename) != NULL;
- if (!exists) {
- /* An ancestor directory of a file ? */
- exists = ContainsMountPoint(filename, -1);
- }
-
- Unlock();
-
- Tcl_SetObjResult(interp, Tcl_NewBooleanObj(exists));
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSInfoObjCmd --
- *
- * This procedure is invoked to process the [zipfs info] command. On
- * success, it returns a Tcl list made up of name of ZIP archive file,
- * size uncompressed, size compressed, and archive offset of a file in
- * the ZIP filesystem.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSInfoObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- char *filename;
- ZipEntry *z;
- int ret;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "filename");
- return TCL_ERROR;
- }
- filename = TclGetString(objv[1]);
- ReadLock();
- z = ZipFSLookup(filename);
- if (z) {
- Tcl_Obj *result = Tcl_GetObjResult(interp);
-
- Tcl_ListObjAppendElement(interp, result,
- Tcl_NewStringObj(z->zipFilePtr->name, -1));
- Tcl_ListObjAppendElement(interp, result,
- Tcl_NewWideIntObj(z->numBytes));
- Tcl_ListObjAppendElement(interp, result,
- Tcl_NewWideIntObj(z->numCompressedBytes));
- Tcl_ListObjAppendElement(interp, result, Tcl_NewWideIntObj(z->offset));
- ret = TCL_OK;
- } else {
- Tcl_SetErrno(ENOENT);
- if (interp) {
- Tcl_SetObjResult(
- interp,
- Tcl_ObjPrintf("path \"%s\" not found in any zipfs volume",
- filename));
- }
- ret = TCL_ERROR;
- }
- Unlock();
- return ret;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSListObjCmd --
- *
- * This procedure is invoked to process the [zipfs list] command. On
- * success, it returns a Tcl list of files of the ZIP filesystem which
- * match a search pattern (glob or regexp).
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSListObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
-{
- char *pattern = NULL;
- Tcl_RegExp regexp = NULL;
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch search;
- Tcl_Obj *result = Tcl_GetObjResult(interp);
- const char *options[] = {"-glob", "-regexp", NULL};
- enum list_options { OPT_GLOB, OPT_REGEXP };
-
- /*
- * Parse arguments.
- */
-
- if (objc > 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "?(-glob|-regexp)? ?pattern?");
- return TCL_ERROR;
- }
- if (objc == 3) {
- int idx;
-
- if (Tcl_GetIndexFromObj(interp, objv[1], options, "option",
- 0, &idx) != TCL_OK) {
- return TCL_ERROR;
- }
- switch (idx) {
- case OPT_GLOB:
- pattern = TclGetString(objv[2]);
- break;
- case OPT_REGEXP:
- regexp = Tcl_RegExpCompile(interp, TclGetString(objv[2]));
- if (!regexp) {
- return TCL_ERROR;
- }
- break;
- }
- } else if (objc == 2) {
- pattern = TclGetString(objv[1]);
- }
-
- /*
- * Scan for matching entries.
- */
-
- ReadLock();
- if (pattern) {
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
-
- if (Tcl_StringMatch(z->name, pattern)) {
- Tcl_ListObjAppendElement(interp, result,
- Tcl_NewStringObj(z->name, -1));
- }
- }
- } else if (regexp) {
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
- hPtr; hPtr = Tcl_NextHashEntry(&search)) {
- ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
-
- if (Tcl_RegExpExec(interp, regexp, z->name, z->name)) {
- Tcl_ListObjAppendElement(interp, result,
- Tcl_NewStringObj(z->name, -1));
- }
- }
- } else {
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
- hPtr; hPtr = Tcl_NextHashEntry(&search)) {
- ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
-
- Tcl_ListObjAppendElement(interp, result,
- Tcl_NewStringObj(z->name, -1));
- }
- }
- Unlock();
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * TclZipfs_TclLibrary --
- *
- * This procedure gets (and possibly finds) the root that Tcl's library
- * files are mounted under.
- *
- * Results:
- * A Tcl object holding the location (with zero refcount), or NULL if no
- * Tcl library can be found.
- *
- * Side effects:
- * May initialise the cache of where such library files are to be found.
- * This cache is never cleared.
- *
- *-------------------------------------------------------------------------
- */
-
-/* Utility routine to centralize housekeeping */
-static Tcl_Obj *
-ScriptLibrarySetup(
- const char *dirName)
-{
- Tcl_Obj *libDirObj = Tcl_NewStringObj(dirName, -1);
- Tcl_Obj *subDirObj, *searchPathObj;
-
- TclNewLiteralStringObj(subDirObj, "encoding");
- Tcl_IncrRefCount(subDirObj);
- TclNewObj(searchPathObj);
- Tcl_ListObjAppendElement(NULL, searchPathObj,
- Tcl_FSJoinToPath(libDirObj, 1, &subDirObj));
- Tcl_DecrRefCount(subDirObj);
- Tcl_IncrRefCount(searchPathObj);
- Tcl_SetEncodingSearchPath(searchPathObj);
- Tcl_DecrRefCount(searchPathObj);
- return libDirObj;
-}
-
-Tcl_Obj *
-TclZipfs_TclLibrary(void)
-{
- Tcl_Obj *vfsInitScript;
- int found;
-#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(STATIC_BUILD)
-# define LIBRARY_SIZE 64
- HMODULE hModule;
- WCHAR wName[MAX_PATH + LIBRARY_SIZE];
- char dllName[(MAX_PATH + LIBRARY_SIZE) * 3];
-#endif /* _WIN32 */
-
- /*
- * Use the cached value if that has been set; we don't want to repeat the
- * searching and mounting.
- */
-
- if (zipfs_literal_tcl_library) {
- return ScriptLibrarySetup(zipfs_literal_tcl_library);
- }
-
- /*
- * Look for the library file system within the executable.
- */
-
- vfsInitScript = Tcl_NewStringObj(ZIPFS_APP_MOUNT "/tcl_library/init.tcl",
- -1);
- Tcl_IncrRefCount(vfsInitScript);
- found = Tcl_FSAccess(vfsInitScript, F_OK);
- Tcl_DecrRefCount(vfsInitScript);
- if (found == TCL_OK) {
- zipfs_literal_tcl_library = ZIPFS_APP_MOUNT "/tcl_library";
- return ScriptLibrarySetup(zipfs_literal_tcl_library);
- }
-
- /*
- * Look for the library file system within the DLL/shared library. Note
- * that we must mount the zip file and dll before releasing to search.
- */
-
-#if !defined(STATIC_BUILD)
-#if defined(_WIN32) || defined(__CYGWIN__)
- hModule = (HMODULE)TclWinGetTclInstance();
- GetModuleFileNameW(hModule, wName, MAX_PATH);
-#ifdef __CYGWIN__
- cygwin_conv_path(3, wName, dllName, sizeof(dllName));
-#else
- WideCharToMultiByte(CP_UTF8, 0, wName, -1, dllName, sizeof(dllName), NULL, NULL);
-#endif
-
- if (ZipfsAppHookFindTclInit(dllName) == TCL_OK) {
- return ScriptLibrarySetup(zipfs_literal_tcl_library);
- }
-#elif !defined(NO_DLFCN_H)
- Dl_info dlinfo;
- if (dladdr((const void *)TclZipfs_TclLibrary, &dlinfo) && (dlinfo.dli_fname != NULL)
- && (ZipfsAppHookFindTclInit(dlinfo.dli_fname) == TCL_OK)) {
- return ScriptLibrarySetup(zipfs_literal_tcl_library);
- }
-#else
- if (ZipfsAppHookFindTclInit(CFG_RUNTIME_LIBDIR "/" CFG_RUNTIME_DLLFILE) == TCL_OK) {
- return ScriptLibrarySetup(zipfs_literal_tcl_library);
- }
-#endif /* _WIN32 */
-#endif /* !defined(STATIC_BUILD) */
-
- /*
- * If anything set the cache (but subsequently failed) go with that
- * anyway.
- */
-
- if (zipfs_literal_tcl_library) {
- return ScriptLibrarySetup(zipfs_literal_tcl_library);
- }
- return NULL;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSTclLibraryObjCmd --
- *
- * This procedure is invoked to process the
- * [::tcl::zipfs::tcl_library_init] command, usually called during the
- * execution of Tcl's interpreter startup. It returns the root that Tcl's
- * library files are mounted under.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * May initialise the cache of where such library files are to be found.
- * This cache is never cleared.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSTclLibraryObjCmd(
- TCL_UNUSED(void *),
- Tcl_Interp *interp, /* Current interpreter. */
- TCL_UNUSED(int) /*objc*/,
- TCL_UNUSED(Tcl_Obj *const *)) /*objv*/
-{
- if (!Tcl_IsSafe(interp)) {
- Tcl_Obj *pResult = TclZipfs_TclLibrary();
-
- if (!pResult) {
- TclNewObj(pResult);
- }
- Tcl_SetObjResult(interp, pResult);
- }
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipChannelClose --
- *
- * This function is called to close a channel.
- *
- * Results:
- * Always TCL_OK.
- *
- * Side effects:
- * Resources are free'd.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipChannelClose(
- void *instanceData,
- TCL_UNUSED(Tcl_Interp *),
- int flags)
-{
- ZipChannel *info = (ZipChannel *) instanceData;
-
- if ((flags & (TCL_CLOSE_READ | TCL_CLOSE_WRITE)) != 0) {
- return EINVAL;
- }
-
- if (info->isEncrypted) {
- info->isEncrypted = 0;
- memset(info->keys, 0, sizeof(info->keys));
- }
- WriteLock();
- if (ZipChannelWritable(info)) {
- /*
- * Copy channel data back into original file in archive.
- */
- ZipEntry *z = info->zipEntryPtr;
- assert(info->ubufToFree && info->ubuf);
- unsigned char *newdata;
- newdata = (unsigned char *)attemptckrealloc(
- info->ubufToFree,
- info->numBytes ? info->numBytes : 1); /* Bug [23dd83ce7c] */
- if (newdata == NULL) {
- /* Could not reallocate, keep existing buffer */
- newdata = info->ubufToFree;
- }
- info->ubufToFree = NULL; /* Now newdata! */
- info->ubuf = NULL;
- info->ubufSize = 0;
-
- /* Replace old content */
- if (z->data) {
- ckfree(z->data);
- }
- z->data = newdata; /* May be NULL when ubufToFree was NULL */
- z->numBytes = z->numCompressedBytes = info->numBytes;
- assert(z->data || z->numBytes == 0);
- z->compressMethod = ZIP_COMPMETH_STORED;
- z->timestamp = time(NULL);
- z->isDirectory = 0;
- z->isEncrypted = 0;
- z->offset = 0;
- z->crc32 = 0;
- }
- info->zipFilePtr->numOpen--;
- Unlock();
- if (info->ubufToFree) {
- assert(info->ubuf);
- ckfree(info->ubufToFree);
- info->ubuf = NULL;
- info->ubufToFree = NULL;
- info->ubufSize = 0;
- }
- ckfree(info);
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipChannelRead --
- *
- * This function is called to read data from channel.
- *
- * Results:
- * Number of bytes read or -1 on error with error number set.
- *
- * Side effects:
- * Data is read and file pointer is advanced.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipChannelRead(
- void *instanceData,
- char *buf,
- int toRead,
- int *errloc)
-{
- ZipChannel *info = (ZipChannel *) instanceData;
- Tcl_Size nextpos;
-
- if (info->isDirectory < 0) {
- /*
- * Special case: when executable combined with ZIP archive file read
- * data in front of ZIP, i.e. the executable itself.
- */
-
- nextpos = info->cursor + toRead;
- if ((size_t)nextpos > info->zipFilePtr->baseOffset) {
- toRead = info->zipFilePtr->baseOffset - info->cursor;
- nextpos = info->zipFilePtr->baseOffset;
- }
- if (toRead == 0) {
- return 0;
- }
- memcpy(buf, info->zipFilePtr->data, toRead);
- info->cursor = nextpos;
- *errloc = 0;
- return toRead;
- }
- if (info->isDirectory) {
- *errloc = EISDIR;
- return -1;
- }
- nextpos = info->cursor + toRead;
- if (nextpos > info->numBytes) {
- toRead = info->numBytes - info->cursor;
- nextpos = info->numBytes;
- }
- if (toRead == 0) {
- return 0;
- }
- if (info->isEncrypted) {
- int i;
- /*
- * TODO - when is this code ever exercised? Cannot reach it from
- * tests. In particular, decryption is always done at channel open
- * to allow for seeks and random reads.
- */
- for (i = 0; i < toRead; i++) {
- int ch = info->ubuf[i + info->cursor];
-
- buf[i] = zdecode(info->keys, crc32tab, ch);
- }
- } else {
- memcpy(buf, info->ubuf + info->cursor, toRead);
- }
- info->cursor = nextpos;
- *errloc = 0;
- return toRead;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipChannelWrite --
- *
- * This function is called to write data into channel.
- *
- * Results:
- * Number of bytes written or -1 on error with error number set.
- *
- * Side effects:
- * Data is written and file pointer is advanced.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipChannelWrite(
- void *instanceData,
- const char *buf,
- int toWrite,
- int *errloc)
-{
- ZipChannel *info = (ZipChannel *) instanceData;
- unsigned long nextpos;
-
- if (!ZipChannelWritable(info)) {
- *errloc = EINVAL;
- return -1;
- }
-
- assert(info->ubuf == info->ubufToFree);
- assert(info->ubufToFree && info->ubufSize > 0);
- assert(info->ubufSize <= info->maxWrite);
- assert(info->numBytes <= info->ubufSize);
- assert(info->cursor <= info->numBytes);
-
- if (toWrite == 0) {
- *errloc = 0;
- return 0;
- }
-
- if (info->mode & O_APPEND) {
- info->cursor = info->numBytes;
- }
-
- if (toWrite > (info->maxWrite - info->cursor)) {
- /* File would grow beyond max size permitted */
- /* Don't do partial writes in error case. Or should we? */
- *errloc = EFBIG;
- return -1;
- }
-
- if (toWrite > (info->ubufSize - info->cursor)) {
- /* grow the buffer. We have already checked will not exceed maxWrite */
- Tcl_Size needed = info->cursor + toWrite;
- /* Tack on a bit for future growth. */
- if (needed < (info->maxWrite - needed/2)) {
- needed += needed / 2;
- } else {
- needed = info->maxWrite;
- }
- unsigned char *newBuf =
- (unsigned char *)attemptckrealloc(info->ubufToFree, needed);
- if (newBuf == NULL) {
- *errloc = ENOMEM;
- return -1;
- }
- info->ubufToFree = newBuf;
- info->ubuf = info->ubufToFree;
- info->ubufSize = needed;
- }
- nextpos = info->cursor + toWrite;
- memcpy(info->ubuf + info->cursor, buf, toWrite);
- info->cursor = nextpos;
- if (info->cursor > info->numBytes) {
- info->numBytes = info->cursor;
- }
- *errloc = 0;
- return toWrite;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipChannelSeek/ZipChannelWideSeek --
- *
- * This function is called to position file pointer of channel.
- *
- * Results:
- * New file position or -1 on error with error number set.
- *
- * Side effects:
- * File pointer is repositioned according to offset and mode.
- *
- *-------------------------------------------------------------------------
- */
-
-static long long
-ZipChannelWideSeek(
- void *instanceData,
- long long offset,
- int mode,
- int *errloc)
-{
- ZipChannel *info = (ZipChannel *) instanceData;
- Tcl_Size end;
-
- if (!ZipChannelWritable(info) && (info->isDirectory < 0)) {
- /*
- * Special case: when executable combined with ZIP archive file, seek
- * within front of ZIP, i.e. the executable itself.
- */
- end = info->zipFilePtr->baseOffset;
- } else if (info->isDirectory) {
- *errloc = EINVAL;
- return -1;
- } else {
- end = info->numBytes;
- }
- switch (mode) {
- case SEEK_CUR:
- offset += info->cursor;
- break;
- case SEEK_END:
- offset += end;
- break;
- case SEEK_SET:
- break;
- default:
- *errloc = EINVAL;
- return -1;
- }
- if (offset < 0 || offset > TCL_SIZE_MAX) {
- *errloc = EINVAL;
- return -1;
- }
- if (ZipChannelWritable(info)) {
- if (offset > info->maxWrite) {
- *errloc = EINVAL;
- return -1;
- }
- if (offset > info->numBytes) {
- info->numBytes = offset;
- }
- } else if (offset > end) {
- *errloc = EINVAL;
- return -1;
- }
- info->cursor = (Tcl_Size) offset;
- return info->cursor;
-}
-
-#if !defined(TCL_NO_DEPRECATED) && (TCL_MAJOR_VERSION < 9)
-static int
-ZipChannelSeek(
- void *instanceData,
- long offset,
- int mode,
- int *errloc)
-{
- return ZipChannelWideSeek(instanceData, offset, mode, errloc);
-}
-#endif
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipChannelWatchChannel --
- *
- * This function is called for event notifications on channel. Does
- * nothing.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static void
-ZipChannelWatchChannel(
- TCL_UNUSED(void *),
- TCL_UNUSED(int) /*mask*/)
-{
- return;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipChannelGetFile --
- *
- * This function is called to retrieve OS handle for channel.
- *
- * Results:
- * Always TCL_ERROR since there's never an OS handle for a file within a
- * ZIP archive.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipChannelGetFile(
- TCL_UNUSED(void *),
- TCL_UNUSED(int) /*direction*/,
- TCL_UNUSED(void **) /*handlePtr*/)
-{
- return TCL_ERROR;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipChannelOpen --
- *
- * This function opens a Tcl_Channel on a file from a mounted ZIP archive
- * according to given open mode (already parsed by caller).
- *
- * Results:
- * Tcl_Channel on success, or NULL on error.
- *
- * Side effects:
- * Memory is allocated, the file from the ZIP archive is uncompressed.
- *
- *-------------------------------------------------------------------------
- */
-
-static Tcl_Channel
-ZipChannelOpen(
- Tcl_Interp *interp, /* Current interpreter. */
- char *filename, /* What are we opening. */
- int mode) /* O_WRONLY O_RDWR O_TRUNC flags */
-{
- ZipEntry *z;
- ZipChannel *info;
- int flags = 0;
- char cname[128];
-
- int wr = (mode & (O_WRONLY | O_RDWR)) != 0;
-
- /* Check for unsupported modes. */
-
- if ((ZipFS.wrmax <= 0) && wr) {
- Tcl_SetErrno(EACCES);
- if (interp) {
- Tcl_SetObjResult(interp,
- Tcl_ObjPrintf("writes not permitted: %s",
- Tcl_PosixError(interp)));
- }
- return NULL;
- }
-
- if ((mode & (O_APPEND|O_TRUNC)) && !wr) {
- Tcl_SetErrno(EINVAL);
- if (interp) {
- Tcl_SetObjResult(interp,
- Tcl_ObjPrintf("Invalid flags 0x%x. O_APPEND and "
- "O_TRUNC require write access: %s",
- mode,
- Tcl_PosixError(interp)));
- }
- return NULL;
- }
-
- /*
- * Is the file there?
- */
-
- WriteLock();
- z = ZipFSLookup(filename);
- if (!z) {
- Tcl_SetErrno(wr ? ENOTSUP : ENOENT);
- if (interp) {
- Tcl_SetObjResult(interp,
- Tcl_ObjPrintf("file \"%s\" not %s: %s",
- filename,
- wr ? "created" : "found",
- Tcl_PosixError(interp)));
- }
- goto error;
- }
-
- if (z->numBytes < 0 || z->numCompressedBytes < 0 ||
- z->offset >= z->zipFilePtr->length) {
- /* Normally this should only happen for zip64. */
- ZIPFS_ERROR(interp, "file size error (may be zip64)");
- ZIPFS_ERROR_CODE(interp, "FILE_SIZE");
- goto error;
- }
-
- /* Do we support opening the file that way? */
-
- if (wr && z->isDirectory) {
- Tcl_SetErrno(EISDIR);
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "unsupported file type: %s",
- Tcl_PosixError(interp)));
- }
- goto error;
- }
- if ((z->compressMethod != ZIP_COMPMETH_STORED)
- && (z->compressMethod != ZIP_COMPMETH_DEFLATED)) {
- ZIPFS_ERROR(interp, "unsupported compression method");
- ZIPFS_ERROR_CODE(interp, "COMP_METHOD");
- goto error;
- }
- if (wr) {
- if ((mode & O_TRUNC) == 0 && !z->data && (z->numBytes > ZipFS.wrmax)) {
- Tcl_SetErrno(EFBIG);
- ZIPFS_POSIX_ERROR(interp, "file size exceeds max writable");
- goto error;
- }
- flags = TCL_WRITABLE;
- if (mode & O_RDWR)
- flags |= TCL_READABLE;
- } else {
- /* Read-only */
- flags |= TCL_READABLE;
- }
-
- if (z->isEncrypted) {
- if (z->numCompressedBytes < ZIP_CRYPT_HDR_LEN) {
- ZIPFS_ERROR(interp,
- "decryption failed: truncated decryption header");
- ZIPFS_ERROR_CODE(interp, "DECRYPT");
- goto error;
- }
- if (z->zipFilePtr->passBuf[0] == 0) {
- ZIPFS_ERROR(interp, "decryption failed - no password provided");
- ZIPFS_ERROR_CODE(interp, "DECRYPT");
- goto error;
- }
- }
-
- info = AllocateZipChannel(interp);
- if (!info) {
- goto error;
- }
- info->zipFilePtr = z->zipFilePtr;
- info->zipEntryPtr = z;
- if (wr) {
- /* Set up a writable channel. */
-
- if (InitWritableChannel(interp, info, z, mode) == TCL_ERROR) {
- ckfree(info);
- goto error;
- }
- } else if (z->data) {
- /* Set up a readable channel for direct data. */
-
- info->numBytes = z->numBytes;
- info->ubuf = z->data;
- info->ubufToFree = NULL; /* Not dynamically allocated */
- info->ubufSize = 0;
- } else {
- /*
- * Set up a readable channel.
- */
-
- if (InitReadableChannel(interp, info, z) == TCL_ERROR) {
- ckfree(info);
- goto error;
- }
- }
-
- if (z->crc32) {
- if (!(z->flags & ZE_F_CRC_COMPARED)) {
- int crc = crc32(0, NULL, info->numBytes);
- crc = crc32(crc, info->ubuf, info->numBytes);
- z->flags |= ZE_F_CRC_COMPARED;
- if (crc == z->crc32) {
- z->flags |= ZE_F_CRC_CORRECT;
- }
- }
- if (!(z->flags & ZE_F_CRC_CORRECT)) {
- ZIPFS_ERROR(interp, "invalid CRC");
- ZIPFS_ERROR_CODE(interp, "CRC_FAILED");
- if (info->ubufToFree) {
- ckfree(info->ubufToFree);
- info->ubufSize = 0;
- }
- ckfree(info);
- goto error;
- }
- }
-
- /*
- * Wrap the ZipChannel into a Tcl_Channel.
- */
-
- snprintf(cname, sizeof(cname), "zipfs_%" TCL_Z_MODIFIER "x_%d", z->offset,
- ZipFS.idCount++);
- z->zipFilePtr->numOpen++;
- Unlock();
- return Tcl_CreateChannel(&ZipChannelType, cname, info, flags);
-
- error:
- Unlock();
- return NULL;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * InitWritableChannel --
- *
- * Assistant for ZipChannelOpen() that sets up a writable channel. It's
- * up to the caller to actually register the channel.
- *
- * Returns:
- * Tcl result code.
- *
- * Side effects:
- * Allocates memory for the implementation of the channel. Writes to the
- * interpreter's result on error.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-InitWritableChannel(
- Tcl_Interp *interp, /* Current interpreter, or NULL (when errors
- * will be silent). */
- ZipChannel *info, /* The channel to set up. */
- ZipEntry *z, /* The zipped file that the channel will write
- * to. */
- int mode) /* O_APPEND, O_TRUNC */
-{
- int i, ch;
- unsigned char *cbuf = NULL;
-
- /*
- * Set up a writable channel.
- */
-
- info->mode = mode;
- info->maxWrite = ZipFS.wrmax;
-
- info->ubufSize = z->numBytes ? z->numBytes : 1;
- info->ubufToFree = (unsigned char *)attemptckalloc(info->ubufSize);
- info->ubuf = info->ubufToFree;
- if (info->ubufToFree == NULL) {
- goto memoryError;
- }
-
- if (z->isEncrypted) {
- assert(z->numCompressedBytes >= ZIP_CRYPT_HDR_LEN); /* caller should have checked*/
- if (DecodeCryptHeader(
- interp, z, info->keys, z->zipFilePtr->data + z->offset) !=
- TCL_OK) {
- goto error_cleanup;
- }
- }
-
- if (mode & O_TRUNC) {
- /*
- * Truncate; nothing there.
- */
-
- info->numBytes = 0;
- z->crc32 = 0; /* Truncated, CRC no longer applicable */
- } else if (z->data) {
- /*
- * Already got uncompressed data.
- */
- assert(info->ubufSize >= z->numBytes);
- memcpy(info->ubuf, z->data, z->numBytes);
- info->numBytes = z->numBytes;
- } else {
- /*
- * Need to uncompress the existing data.
- */
-
- unsigned char *zbuf = z->zipFilePtr->data + z->offset;
-
- if (z->isEncrypted) {
- zbuf += ZIP_CRYPT_HDR_LEN;
- }
-
- if (z->compressMethod == ZIP_COMPMETH_DEFLATED) {
- z_stream stream;
- int err;
-
- memset(&stream, 0, sizeof(z_stream));
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
- stream.avail_in = z->numCompressedBytes;
- if (z->isEncrypted) {
- unsigned int j;
-
- /* Min length ZIP_CRYPT_HDR_LEN for keys should already been checked. */
- assert(stream.avail_in >= ZIP_CRYPT_HDR_LEN);
-
- stream.avail_in -= ZIP_CRYPT_HDR_LEN;
- cbuf = (unsigned char *) attemptckalloc(stream.avail_in ? stream.avail_in : 1);
- if (!cbuf) {
- goto memoryError;
- }
- for (j = 0; j < stream.avail_in; j++) {
- ch = zbuf[j];
- cbuf[j] = zdecode(info->keys, crc32tab, ch);
- }
- stream.next_in = cbuf;
- } else {
- stream.next_in = zbuf;
- }
- stream.next_out = info->ubuf;
- stream.avail_out = info->ubufSize;
- if (inflateInit2(&stream, -15) != Z_OK) {
- goto corruptionError;
- }
- err = inflate(&stream, Z_SYNC_FLUSH);
- inflateEnd(&stream);
- if ((err != Z_STREAM_END) &&
- ((err != Z_OK) || (stream.avail_in != 0))) {
- goto corruptionError;
- }
- /* Even if decompression succeeded, counts should be as expected */
- if ((int) stream.total_out != z->numBytes)
- goto corruptionError;
- info->numBytes = z->numBytes;
- if (cbuf) {
- ckfree(cbuf);
- }
- } else if (z->isEncrypted) {
- /*
- * Need to decrypt some otherwise-simple stored data.
- */
- if (z->numCompressedBytes <= ZIP_CRYPT_HDR_LEN ||
- (z->numCompressedBytes - ZIP_CRYPT_HDR_LEN) != z->numBytes)
- goto corruptionError;
- int len = z->numCompressedBytes - ZIP_CRYPT_HDR_LEN;
- assert(len <= info->ubufSize);
- for (i = 0; i < len; i++) {
- ch = zbuf[i];
- info->ubuf[i] = zdecode(info->keys, crc32tab, ch);
- }
- info->numBytes = len;
- }
- else {
- /*
- * Simple stored data. Copy into our working buffer.
- */
- assert(info->ubufSize >= z->numBytes);
- memcpy(info->ubuf, zbuf, z->numBytes);
- info->numBytes = z->numBytes;
- }
- memset(info->keys, 0, sizeof(info->keys));
- }
- if (mode & O_APPEND) {
- info->cursor = info->numBytes;
- }
-
- return TCL_OK;
-
- memoryError:
- ZIPFS_MEM_ERROR(interp);
- goto error_cleanup;
-
- corruptionError:
- if (cbuf) {
- memset(info->keys, 0, sizeof(info->keys));
- ckfree(cbuf);
- }
- ZIPFS_ERROR(interp, "decompression error");
- ZIPFS_ERROR_CODE(interp, "CORRUPT");
-
- error_cleanup:
- if (info->ubufToFree) {
- ckfree(info->ubufToFree);
- info->ubufToFree = NULL;
- info->ubuf = NULL;
- info->ubufSize = 0;
- }
- return TCL_ERROR;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * InitReadableChannel --
- *
- * Assistant for ZipChannelOpen() that sets up a readable channel. It's
- * up to the caller to actually register the channel. Caller should have
- * validated the passed ZipEntry (byte counts in particular)
- *
- * Returns:
- * Tcl result code.
- *
- * Side effects:
- * Allocates memory for the implementation of the channel. Writes to the
- * interpreter's result on error.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-InitReadableChannel(
- Tcl_Interp *interp, /* Current interpreter, or NULL (when errors
- * will be silent). */
- ZipChannel *info, /* The channel to set up. */
- ZipEntry *z) /* The zipped file that the channel will read
- * from. */
-{
- unsigned char *ubuf = NULL;
- int ch;
-
- info->iscompr = (z->compressMethod == ZIP_COMPMETH_DEFLATED);
- info->ubuf = z->zipFilePtr->data + z->offset;
- info->ubufToFree = NULL; /* ubuf memory not allocated */
- info->ubufSize = 0;
- info->isDirectory = z->isDirectory;
- info->isEncrypted = z->isEncrypted;
- info->mode = O_RDONLY;
-
- /* Caller must validate - bug [6ed3447a7e] */
- assert(z->numBytes >= 0 && z->numCompressedBytes >= 0);
- info->numBytes = z->numBytes;
-
- if (info->isEncrypted) {
- assert(z->numCompressedBytes >= ZIP_CRYPT_HDR_LEN); /* caller should have checked*/
- if (DecodeCryptHeader(interp, z, info->keys, info->ubuf) != TCL_OK) {
- goto error_cleanup;
- }
- info->ubuf += ZIP_CRYPT_HDR_LEN;
- }
-
- if (info->iscompr) {
- z_stream stream;
- int err;
- unsigned int j;
-
- /*
- * Data to decode is compressed, and possibly encrpyted too. If
- * encrypted, local variable ubuf is used to hold the decrypted but
- * still compressed data.
- */
-
- memset(&stream, 0, sizeof(z_stream));
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
- stream.avail_in = z->numCompressedBytes;
- if (info->isEncrypted) {
- assert(stream.avail_in >= ZIP_CRYPT_HDR_LEN);
- stream.avail_in -= ZIP_CRYPT_HDR_LEN;
- ubuf = (unsigned char *) attemptckalloc(stream.avail_in ? stream.avail_in : 1);
- if (!ubuf) {
- goto memoryError;
- }
-
- for (j = 0; j < stream.avail_in; j++) {
- ch = info->ubuf[j];
- ubuf[j] = zdecode(info->keys, crc32tab, ch);
- }
- stream.next_in = ubuf;
- } else {
- stream.next_in = info->ubuf;
- }
-
- info->ubufSize = info->numBytes ? info->numBytes : 1;
- info->ubufToFree = (unsigned char *)attemptckalloc(info->ubufSize);
- info->ubuf = info->ubufToFree;
- stream.next_out = info->ubuf;
- if (!info->ubuf) {
- goto memoryError;
- }
- stream.avail_out = info->numBytes;
- if (inflateInit2(&stream, -15) != Z_OK) {
- goto corruptionError;
- }
- err = inflate(&stream, Z_SYNC_FLUSH);
- inflateEnd(&stream);
-
- /*
- * Decompression was successful if we're either in the END state, or
- * in the OK state with no buffered bytes.
- */
-
- if ((err != Z_STREAM_END)
- && ((err != Z_OK) || (stream.avail_in != 0))) {
- goto corruptionError;
- }
- /* Even if decompression succeeded, counts should be as expected */
- if ((int) stream.total_out != z->numBytes)
- goto corruptionError;
-
- if (ubuf) {
- info->isEncrypted = 0;
- memset(info->keys, 0, sizeof(info->keys));
- ckfree(ubuf);
- }
- } else if (info->isEncrypted) {
- unsigned int j, len;
-
- /*
- * Decode encrypted but uncompressed file, since we support Tcl_Seek()
- * on it, and it can be randomly accessed later.
- */
- if (z->numCompressedBytes <= ZIP_CRYPT_HDR_LEN ||
- (z->numCompressedBytes - ZIP_CRYPT_HDR_LEN) != z->numBytes)
- goto corruptionError;
- len = z->numCompressedBytes - ZIP_CRYPT_HDR_LEN;
- ubuf = (unsigned char *) attemptckalloc(len);
- if (ubuf == NULL) {
- goto memoryError;
- }
- for (j = 0; j < len; j++) {
- ch = info->ubuf[j];
- ubuf[j] = zdecode(info->keys, crc32tab, ch);
- }
- info->ubufSize = len;
- info->ubufToFree = ubuf;
- info->ubuf = info->ubufToFree;
- ubuf = NULL; /* So it does not inadvertently get free on future changes */
- info->isEncrypted = 0;
- }
- return TCL_OK;
-
- corruptionError:
- ZIPFS_ERROR(interp, "decompression error");
- ZIPFS_ERROR_CODE(interp, "CORRUPT");
- goto error_cleanup;
-
- memoryError:
- ZIPFS_MEM_ERROR(interp);
-
- error_cleanup:
- if (ubuf) {
- memset(info->keys, 0, sizeof(info->keys));
- ckfree(ubuf);
- }
- if (info->ubufToFree) {
- ckfree(info->ubufToFree);
- info->ubufToFree = NULL;
- info->ubuf = NULL;
- info->ubufSize = 0;
- }
-
- return TCL_ERROR;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipEntryStat --
- *
- * This function implements the ZIP filesystem specific version of the
- * library version of stat.
- *
- * Results:
- * See stat documentation.
- *
- * Side effects:
- * See stat documentation.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipEntryStat(
- char *path,
- Tcl_StatBuf *buf)
-{
- ZipEntry *z;
- int ret;
-
- ReadLock();
- z = ZipFSLookup(path);
- if (z) {
- memset(buf, 0, sizeof(Tcl_StatBuf));
- if (z->isDirectory) {
- buf->st_mode = S_IFDIR | 0555;
- } else {
- buf->st_mode = S_IFREG | 0555;
- }
- buf->st_size = z->numBytes;
- buf->st_mtime = z->timestamp;
- buf->st_ctime = z->timestamp;
- buf->st_atime = z->timestamp;
- ret = 0;
- } else if (ContainsMountPoint(path, -1)) {
- /* An intermediate dir under which a mount exists */
- memset(buf, 0, sizeof(Tcl_StatBuf));
- Tcl_Time t;
- Tcl_GetTime(&t);
- buf->st_atime = buf->st_mtime = buf->st_ctime = t.sec;
- buf->st_mode = S_IFDIR | 0555;
- ret = 0;
- } else {
- Tcl_SetErrno(ENOENT);
- ret = -1;
- }
- Unlock();
- return ret;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipEntryAccess --
- *
- * This function implements the ZIP filesystem specific version of the
- * library version of access.
- *
- * Results:
- * See access documentation.
- *
- * Side effects:
- * See access documentation.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipEntryAccess(
- char *path,
- int mode)
-{
- if (mode & X_OK) {
- return -1;
- }
-
- ReadLock();
- int access;
- ZipEntry *z = ZipFSLookup(path);
- if (z) {
- /* Currently existing files read/write but dirs are read-only */
- access = (z->isDirectory && (mode & W_OK)) ? -1 : 0;
- } else {
- if (mode & W_OK) {
- access = -1;
- } else {
- /*
- * Even if entry does not exist, could be intermediate dir
- * containing a mount point
- */
- access = ContainsMountPoint(path, -1) ? 0 : -1;
- }
- }
- Unlock();
- return access;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSOpenFileChannelProc --
- *
- * Open a channel to a file in a mounted ZIP archive. Delegates to
- * ZipChannelOpen().
- *
- * Results:
- * Tcl_Channel on success, or NULL on error.
- *
- * Side effects:
- * Allocates memory.
- *
- *-------------------------------------------------------------------------
- */
-
-static Tcl_Channel
-ZipFSOpenFileChannelProc(
- Tcl_Interp *interp, /* Current interpreter. */
- Tcl_Obj *pathPtr,
- int mode,
- TCL_UNUSED(int) /* permissions */)
-{
- pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
- if (!pathPtr) {
- return NULL;
- }
-
- return ZipChannelOpen(interp, TclGetString(pathPtr), mode);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSStatProc --
- *
- * This function implements the ZIP filesystem specific version of the
- * library version of stat.
- *
- * Results:
- * See stat documentation.
- *
- * Side effects:
- * See stat documentation.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSStatProc(
- Tcl_Obj *pathPtr,
- Tcl_StatBuf *buf)
-{
- pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
- if (!pathPtr) {
- return -1;
- }
- return ZipEntryStat(TclGetString(pathPtr), buf);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSAccessProc --
- *
- * This function implements the ZIP filesystem specific version of the
- * library version of access.
- *
- * Results:
- * See access documentation.
- *
- * Side effects:
- * See access documentation.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSAccessProc(
- Tcl_Obj *pathPtr,
- int mode)
-{
- pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
- if (!pathPtr) {
- return -1;
- }
- return ZipEntryAccess(TclGetString(pathPtr), mode);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSFilesystemSeparatorProc --
- *
- * This function returns the separator to be used for a given path. The
- * object returned should have a refCount of zero
- *
- * Results:
- * A Tcl object, with a refCount of zero. If the caller needs to retain a
- * reference to the object, it should call Tcl_IncrRefCount, and should
- * otherwise free the object.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static Tcl_Obj *
-ZipFSFilesystemSeparatorProc(
- TCL_UNUSED(Tcl_Obj *) /*pathPtr*/)
-{
- return Tcl_NewStringObj("/", -1);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * AppendWithPrefix --
- *
- * Worker for ZipFSMatchInDirectoryProc() that is a wrapper around
- * Tcl_ListObjAppendElement() which knows about handling prefixes.
- *
- *-------------------------------------------------------------------------
- */
-
-static inline void
-AppendWithPrefix(
- Tcl_Obj *result, /* Where to append a list element to. */
- Tcl_DString *prefix, /* The prefix to add to the element, or NULL
- * for don't do that. */
- const char *name, /* The name to append. */
- Tcl_Size nameLen) /* The length of the name. May be < 0 for
- * append-up-to-NUL-byte. */
-{
- if (prefix) {
- Tcl_Size prefixLength = Tcl_DStringLength(prefix);
-
- Tcl_DStringAppend(prefix, name, nameLen);
- Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(
- Tcl_DStringValue(prefix), Tcl_DStringLength(prefix)));
- Tcl_DStringSetLength(prefix, prefixLength);
- } else {
- Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(name, nameLen));
- }
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSMatchInDirectoryProc --
- *
- * This routine is used by the globbing code to search a directory for
- * all files which match a given pattern.
- *
- * Results:
- * The return value is a standard Tcl result indicating whether an error
- * occurred in globbing. Errors are left in interp, good results are
- * lappend'ed to result (which must be a valid object).
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSMatchInDirectoryProc(
- Tcl_Interp *interp,
- Tcl_Obj *result, /* Where to append matched items to. */
- Tcl_Obj *pathPtr, /* Where we are looking. */
- const char *pattern, /* What names we are looking for. */
- Tcl_GlobTypeData *types) /* What types we are looking for. */
-{
- Tcl_Obj *normPathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
- int scnt, l;
- Tcl_Size prefixLen, len, strip = 0;
- char *pat, *prefix, *path;
- Tcl_DString dsPref, *prefixBuf = NULL;
- int foundInHash, notDuplicate;
- ZipEntry *z;
- int wanted; /* TCL_GLOB_TYPE* */
-
- if (!normPathPtr) {
- return -1;
- }
- if (types) {
- wanted = types->type;
- if ((wanted & TCL_GLOB_TYPE_MOUNT) && (wanted != TCL_GLOB_TYPE_MOUNT)) {
- if (interp) {
- ZIPFS_ERROR(interp,
- "Internal error: TCL_GLOB_TYPE_MOUNT should not "
- "be set in conjunction with other glob types.");
- }
- return TCL_ERROR;
- }
- if ((wanted & (TCL_GLOB_TYPE_DIR | TCL_GLOB_TYPE_FILE |
- TCL_GLOB_TYPE_MOUNT)) == 0) {
- /* Not looking for files,dirs,mounts. zipfs cannot have others */
- return TCL_OK;
- }
- wanted &=
- (TCL_GLOB_TYPE_DIR | TCL_GLOB_TYPE_FILE | TCL_GLOB_TYPE_MOUNT);
- }
- else {
- wanted = TCL_GLOB_TYPE_DIR | TCL_GLOB_TYPE_FILE;
- }
-
- /*
- * The prefix that gets prepended to results.
- */
-
- prefix = TclGetStringFromObj(pathPtr, &prefixLen);
-
- /*
- * The (normalized) path we're searching.
- */
-
- path = TclGetStringFromObj(normPathPtr, &len);
-
- Tcl_DStringInit(&dsPref);
- if (strcmp(prefix, path) == 0) {
- prefixBuf = NULL;
- } else {
- /*
- * We need to strip the normalized prefix of the filenames and replace
- * it with the official prefix that we were expecting to get.
- */
-
- strip = len + 1;
- Tcl_DStringAppend(&dsPref, prefix, prefixLen);
- Tcl_DStringAppend(&dsPref, "/", 1);
- prefix = Tcl_DStringValue(&dsPref);
- prefixBuf = &dsPref;
- }
-
- ReadLock();
-
- /*
- * Are we globbing the mount points?
- */
-
- if (wanted & TCL_GLOB_TYPE_MOUNT) {
- ZipFSMatchMountPoints(result, normPathPtr, pattern, prefixBuf);
- goto end;
- }
-
- /* Should not reach here unless at least one of DIR or FILE is set */
- assert(wanted & (TCL_GLOB_TYPE_DIR | TCL_GLOB_TYPE_FILE));
-
- /* Does the path exist in the hash table? */
- z = ZipFSLookup(path);
- if (z) {
- /*
- * Can we skip the complexity of actual globbing? Without a pattern,
- * yes; it's a directory existence test.
- */
- if (!pattern || (pattern[0] == '\0')) {
- /* TODO - can't seem to get to this code from script for tests. */
- /* Follow logic of what tclUnixFile.c does */
- if ((wanted == (TCL_GLOB_TYPE_DIR | TCL_GLOB_TYPE_FILE)) ||
- (wanted == TCL_GLOB_TYPE_DIR && z->isDirectory) ||
- (wanted == TCL_GLOB_TYPE_FILE && !z->isDirectory)) {
- Tcl_ListObjAppendElement(NULL, result, pathPtr);
- }
- goto end;
- }
- } else {
- /* Not in the hash table but could be an intermediate dir in a mount */
- if (!pattern || (pattern[0] == '\0')) {
- /* TODO - can't seem to get to this code from script for tests. */
- if ((wanted & TCL_GLOB_TYPE_DIR) && ContainsMountPoint(path, len)) {
- Tcl_ListObjAppendElement(NULL, result, pathPtr);
- }
- goto end;
- }
- }
-
- foundInHash = (z != NULL);
-
- /*
- * We've got to work for our supper and do the actual globbing. And all
- * we've got really is an undifferentiated pile of all the filenames we've
- * got from all our ZIP mounts.
- */
-
- l = strlen(pattern);
- pat = (char *) ckalloc(len + l + 2);
- memcpy(pat, path, len);
- while ((len > 1) && (pat[len - 1] == '/')) {
- --len;
- }
- if ((len > 1) || (pat[0] != '/')) {
- pat[len] = '/';
- ++len;
- }
- memcpy(pat + len, pattern, l + 1);
- scnt = CountSlashes(pat);
-
- Tcl_HashTable duplicates;
- notDuplicate = 0;
- Tcl_InitHashTable(&duplicates, TCL_STRING_KEYS);
-
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch search;
- if (foundInHash) {
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- z = (ZipEntry *)Tcl_GetHashValue(hPtr);
-
- if ((wanted == (TCL_GLOB_TYPE_DIR | TCL_GLOB_TYPE_FILE)) ||
- (wanted == TCL_GLOB_TYPE_DIR && z->isDirectory) ||
- (wanted == TCL_GLOB_TYPE_FILE && !z->isDirectory)) {
- if ((z->depth == scnt) &&
- ((z->flags & ZE_F_VOLUME) == 0) /* Bug 14db54d81e */
- && Tcl_StringCaseMatch(z->name, pat, 0)) {
- Tcl_CreateHashEntry(
- &duplicates, z->name + strip, &notDuplicate);
- assert(notDuplicate);
- AppendWithPrefix(result, prefixBuf, z->name + strip, -1);
- }
- }
- }
- }
- if (wanted & TCL_GLOB_TYPE_DIR) {
- /*
- * Also check paths that are ancestors of a mount. e.g. glob
- * //zipfs:/a/? with mount at //zipfs:/a/b/c. Also have to be
- * careful about duplicates, such as when another mount is
- * //zipfs:/a/b/d
- */
- Tcl_DString ds;
- Tcl_DStringInit(&ds);
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- ZipFile *zf = (ZipFile *)Tcl_GetHashValue(hPtr);
- if (Tcl_StringCaseMatch(zf->mountPoint, pat, 0)) {
- const char *tail = zf->mountPoint + len;
- if (*tail == '\0')
- continue;
- const char *end = strchr(tail, '/');
- Tcl_DStringAppend(&ds,
- zf->mountPoint + strip,
- end ? (Tcl_Size)(end - zf->mountPoint) : -1);
- const char *matchedPath = Tcl_DStringValue(&ds);
- (void)Tcl_CreateHashEntry(
- &duplicates, matchedPath, &notDuplicate);
- if (notDuplicate) {
- AppendWithPrefix(
- result, prefixBuf, matchedPath, Tcl_DStringLength(&ds));
- }
- Tcl_DStringFree(&ds);
- }
- }
- }
- Tcl_DeleteHashTable(&duplicates);
- ckfree(pat);
-
- end:
- Unlock();
- Tcl_DStringFree(&dsPref);
- return TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSMatchMountPoints --
- *
- * This routine is a worker for ZipFSMatchInDirectoryProc, used by the
- * globbing code to search for all mount points files which match a given
- * pattern.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Adds the matching mounts to the list in result, uses prefix as working
- * space if it is non-NULL.
- *
- *-------------------------------------------------------------------------
- */
-
-static void
-ZipFSMatchMountPoints(
- Tcl_Obj *result, /* The list of matches being built. */
- Tcl_Obj *normPathPtr, /* Where we're looking from. */
- const char *pattern, /* What we're looking for. NULL for a full
- * list. */
- Tcl_DString *prefix) /* Workspace filled with a prefix for all the
- * filenames, or NULL if no prefix is to be
- * used. */
-{
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch search;
- size_t l;
- Tcl_Size normLength;
- const char *path = TclGetStringFromObj(normPathPtr, &normLength);
- Tcl_Size len = normLength;
-
- if (len < 1) {
- /*
- * Shouldn't happen. But "shouldn't"...
- */
-
- return;
- }
- l = CountSlashes(path);
- if (path[len - 1] == '/') {
- len--;
- } else {
- l++;
- }
- if (!pattern || (pattern[0] == '\0')) {
- pattern = "*";
- }
-
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- ZipFile *zf = (ZipFile *) Tcl_GetHashValue(hPtr);
-
- if (zf->mountPointLen == 0) {
- ZipEntry *z;
-
- /*
- * Enumerate the contents of the ZIP; it's mounted on the root.
- * TODO - a holdover from androwish? Tcl does not allow mounting
- * outside of the //zipfs:/ area.
- */
-
- for (z = zf->topEnts; z; z = z->tnext) {
- Tcl_Size lenz = strlen(z->name);
-
- if ((lenz > len + 1) && (strncmp(z->name, path, len) == 0)
- && (z->name[len] == '/')
- && (CountSlashes(z->name) == l)
- && Tcl_StringCaseMatch(z->name + len + 1, pattern, 0)) {
- AppendWithPrefix(result, prefix, z->name, lenz);
- }
- }
- } else if ((zf->mountPointLen > len + 1)
- && (strncmp(zf->mountPoint, path, len) == 0)
- && (zf->mountPoint[len] == '/')
- && (CountSlashes(zf->mountPoint) == l)
- && Tcl_StringCaseMatch(zf->mountPoint + len + 1,
- pattern, 0)) {
- /*
- * Standard mount; append if it matches.
- */
-
- AppendWithPrefix(result, prefix, zf->mountPoint, zf->mountPointLen);
- }
- }
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSPathInFilesystemProc --
- *
- * This function determines if the given path object is in the ZIP
- * filesystem.
- *
- * Results:
- * TCL_OK when the path object is in the ZIP filesystem, -1 otherwise.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSPathInFilesystemProc(
- Tcl_Obj *pathPtr,
- TCL_UNUSED(void **))
-{
- Tcl_Size len;
- char *path;
-
- pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
- if (!pathPtr) {
- return -1;
- }
- path = TclGetStringFromObj(pathPtr, &len);
-
- /*
- * Claim any path under ZIPFS_VOLUME as ours. This is both a necessary
- * and sufficient condition as zipfs mounts at arbitrary paths are
- * not permitted (unlike Androwish).
- */
- return strncmp(path, ZIPFS_VOLUME, ZIPFS_VOLUME_LEN) ? -1 : TCL_OK;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSListVolumesProc --
- *
- * Lists the currently mounted ZIP filesystem volumes.
- *
- * Results:
- * The list of volumes.
- *
- * Side effects:
- * None
- *
- *-------------------------------------------------------------------------
- */
-
-static Tcl_Obj *
-ZipFSListVolumesProc(void)
-{
- return Tcl_NewStringObj(ZIPFS_VOLUME, -1);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSFileAttrStringsProc --
- *
- * This function implements the ZIP filesystem dependent 'file
- * attributes' subcommand, for listing the set of possible attribute
- * strings.
- *
- * Results:
- * An array of strings
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-enum ZipFileAttrs {
- ZIP_ATTR_UNCOMPSIZE,
- ZIP_ATTR_COMPSIZE,
- ZIP_ATTR_OFFSET,
- ZIP_ATTR_MOUNT,
- ZIP_ATTR_ARCHIVE,
- ZIP_ATTR_PERMISSIONS,
- ZIP_ATTR_CRC
-};
-
-static const char *const *
-ZipFSFileAttrStringsProc(
- TCL_UNUSED(Tcl_Obj *) /*pathPtr*/,
- TCL_UNUSED(Tcl_Obj **) /*objPtrRef*/)
-{
- /*
- * Must match up with ZipFileAttrs enum above.
- */
-
- static const char *const attrs[] = {
- "-uncompsize",
- "-compsize",
- "-offset",
- "-mount",
- "-archive",
- "-permissions",
- "-crc",
- NULL,
- };
-
- return attrs;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSFileAttrsGetProc --
- *
- * This function implements the ZIP filesystem specific 'file attributes'
- * subcommand, for 'get' operations.
- *
- * Results:
- * Standard Tcl return code. The object placed in objPtrRef (if TCL_OK
- * was returned) is likely to have a refCount of zero. Either way we must
- * either store it somewhere (e.g. the Tcl result), or Incr/Decr its
- * refCount to ensure it is properly freed.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSFileAttrsGetProc(
- Tcl_Interp *interp, /* Current interpreter. */
- int index,
- Tcl_Obj *pathPtr,
- Tcl_Obj **objPtrRef)
-{
- Tcl_Size len;
- int ret = TCL_OK;
- char *path;
- ZipEntry *z;
-
- pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
- if (!pathPtr) {
- return -1;
- }
- path = TclGetStringFromObj(pathPtr, &len);
- ReadLock();
- z = ZipFSLookup(path);
- if (!z && !ContainsMountPoint(path, -1)) {
- Tcl_SetErrno(ENOENT);
- ZIPFS_POSIX_ERROR(interp, "file not found");
- ret = TCL_ERROR;
- goto done;
- }
- /* z == NULL for intermediate directories that are ancestors of mounts */
- switch (index) {
- case ZIP_ATTR_UNCOMPSIZE:
- TclNewIntObj(*objPtrRef, z ? z->numBytes : 0);
- break;
- case ZIP_ATTR_COMPSIZE:
- TclNewIntObj(*objPtrRef, z ? z->numCompressedBytes : 0);
- break;
- case ZIP_ATTR_OFFSET:
- TclNewIntObj(*objPtrRef, z ? z->offset : 0);
- break;
- case ZIP_ATTR_MOUNT:
- if (z) {
- *objPtrRef = Tcl_NewStringObj(z->zipFilePtr->mountPoint,
- z->zipFilePtr->mountPointLen);
- } else {
- *objPtrRef = Tcl_NewStringObj("", 0);
- }
- break;
- case ZIP_ATTR_ARCHIVE:
- *objPtrRef = Tcl_NewStringObj(z ? z->zipFilePtr->name : "", -1);
- break;
- case ZIP_ATTR_PERMISSIONS:
- *objPtrRef = Tcl_NewStringObj("0o555", -1);
- break;
- case ZIP_ATTR_CRC:
- TclNewIntObj(*objPtrRef, z ? z->crc32 : 0);
- break;
- default:
- ZIPFS_ERROR(interp, "unknown attribute");
- ZIPFS_ERROR_CODE(interp, "FILE_ATTR");
- ret = TCL_ERROR;
- }
-
- done:
- Unlock();
- return ret;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSFileAttrsSetProc --
- *
- * This function implements the ZIP filesystem specific 'file attributes'
- * subcommand, for 'set' operations.
- *
- * Results:
- * Standard Tcl return code.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSFileAttrsSetProc(
- Tcl_Interp *interp, /* Current interpreter. */
- TCL_UNUSED(int) /*index*/,
- TCL_UNUSED(Tcl_Obj *) /*pathPtr*/,
- TCL_UNUSED(Tcl_Obj *) /*objPtr*/)
-{
- ZIPFS_ERROR(interp, "unsupported operation");
- ZIPFS_ERROR_CODE(interp, "UNSUPPORTED_OP");
- return TCL_ERROR;
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSFilesystemPathTypeProc --
- *
- * Results:
- *
- * Side effects:
- *
- *-------------------------------------------------------------------------
- */
-
-static Tcl_Obj *
-ZipFSFilesystemPathTypeProc(
- TCL_UNUSED(Tcl_Obj *) /*pathPtr*/)
-{
- return Tcl_NewStringObj("zip", -1);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * ZipFSLoadFile --
- *
- * This functions deals with loading native object code. If the given
- * path object refers to a file within the ZIP filesystem, an approriate
- * error code is returned to delegate loading to the caller (by copying
- * the file to temp store and loading from there). As fallback when the
- * file refers to the ZIP file system but is not present, it is looked up
- * relative to the executable and loaded from there when available.
- *
- * Results:
- * TCL_OK on success, TCL_ERROR otherwise with error message left.
- *
- * Side effects:
- * Loads native code into the process address space.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-ZipFSLoadFile(
- Tcl_Interp *interp, /* Current interpreter. */
- Tcl_Obj *path,
- Tcl_LoadHandle *loadHandle,
- Tcl_FSUnloadFileProc **unloadProcPtr,
- int flags)
-{
- Tcl_FSLoadFileProc2 *loadFileProc;
-#ifdef ANDROID
- /*
- * Force loadFileProc to native implementation since the package manager
- * already extracted the shared libraries from the APK at install time.
- */
-
- loadFileProc = (Tcl_FSLoadFileProc2 *) tclNativeFilesystem.loadFileProc;
- if (loadFileProc) {
- return loadFileProc(interp, path, loadHandle, unloadProcPtr, flags);
- }
- Tcl_SetErrno(ENOENT);
- ZIPFS_ERROR(interp, Tcl_PosixError(interp));
- return TCL_ERROR;
-#else /* !ANDROID */
- Tcl_Obj *altPath = NULL;
- int ret = TCL_ERROR;
- Tcl_Obj *objs[2] = { NULL, NULL };
-
- if (Tcl_FSAccess(path, R_OK) == 0) {
- /*
- * EXDEV should trigger loading by copying to temp store.
- */
-
- Tcl_SetErrno(EXDEV);
- ZIPFS_ERROR(interp, Tcl_PosixError(interp));
- return ret;
- }
-
- objs[1] = TclPathPart(interp, path, TCL_PATH_DIRNAME);
- if (objs[1] && (ZipFSAccessProc(objs[1], R_OK) == 0)) {
- const char *execName = Tcl_GetNameOfExecutable();
-
- /*
- * Shared object is not in ZIP but its path prefix is, thus try to
- * load from directory where the executable came from.
- */
-
- TclDecrRefCount(objs[1]);
- objs[1] = TclPathPart(interp, path, TCL_PATH_TAIL);
-
- /*
- * Get directory name of executable manually to deal with cases where
- * [file dirname [info nameofexecutable]] is equal to [info
- * nameofexecutable] due to VFS effects.
- */
-
- if (execName) {
- const char *p = strrchr(execName, '/');
-
- if (p && p > execName + 1) {
- --p;
- objs[0] = Tcl_NewStringObj(execName, p - execName);
- }
- }
- if (!objs[0]) {
- objs[0] = TclPathPart(interp, TclGetObjNameOfExecutable(),
- TCL_PATH_DIRNAME);
- }
- if (objs[0]) {
- altPath = TclJoinPath(2, objs, 0);
- if (altPath) {
- Tcl_IncrRefCount(altPath);
- if (Tcl_FSAccess(altPath, R_OK) == 0) {
- path = altPath;
- }
- }
- }
- }
- if (objs[0]) {
- Tcl_DecrRefCount(objs[0]);
- }
- if (objs[1]) {
- Tcl_DecrRefCount(objs[1]);
- }
-
- loadFileProc = (Tcl_FSLoadFileProc2 *) (void *)
- tclNativeFilesystem.loadFileProc;
- if (loadFileProc) {
- ret = loadFileProc(interp, path, loadHandle, unloadProcPtr, flags);
- } else {
- Tcl_SetErrno(ENOENT);
- ZIPFS_ERROR(interp, Tcl_PosixError(interp));
- }
- if (altPath) {
- Tcl_DecrRefCount(altPath);
- }
- return ret;
-#endif /* ANDROID */
-}
-
-#endif /* HAVE_ZLIB */
-
-/*
- *-------------------------------------------------------------------------
- *
- * TclZipfs_Init --
- *
- * Perform per interpreter initialization of this module.
- *
- * Results:
- * The return value is a standard Tcl result.
- *
- * Side effects:
- * Initializes this module if not already initialized, and adds module
- * related commands to the given interpreter.
- *
- *-------------------------------------------------------------------------
- */
-
-int
-TclZipfs_Init(
- Tcl_Interp *interp) /* Current interpreter. */
-{
-#ifdef HAVE_ZLIB
- static const EnsembleImplMap initMap[] = {
- {"mkimg", ZipFSMkImgObjCmd, NULL, NULL, NULL, 1},
- {"mkzip", ZipFSMkZipObjCmd, NULL, NULL, NULL, 1},
- {"lmkimg", ZipFSLMkImgObjCmd, NULL, NULL, NULL, 1},
- {"lmkzip", ZipFSLMkZipObjCmd, NULL, NULL, NULL, 1},
- /* The 4 entries above are not available in safe interpreters */
- {"mount", ZipFSMountObjCmd, NULL, NULL, NULL, 1},
- {"mount_data", ZipFSMountBufferObjCmd, NULL, NULL, NULL, 1},
- {"unmount", ZipFSUnmountObjCmd, NULL, NULL, NULL, 1},
- {"mkkey", ZipFSMkKeyObjCmd, NULL, NULL, NULL, 1},
- {"exists", ZipFSExistsObjCmd, NULL, NULL, NULL, 0},
- {"info", ZipFSInfoObjCmd, NULL, NULL, NULL, 0},
- {"list", ZipFSListObjCmd, NULL, NULL, NULL, 0},
- {"canonical", ZipFSCanonicalObjCmd, NULL, NULL, NULL, 0},
- {"root", ZipFSRootObjCmd, NULL, NULL, NULL, 0},
- {NULL, NULL, NULL, NULL, NULL, 0}
- };
- static const char findproc[] =
- "namespace eval ::tcl::zipfs {}\n"
- "proc ::tcl::zipfs::Find dir {\n"
- " set result {}\n"
- " if {[catch {glob -directory $dir -nocomplain * .*} list]} {\n"
- " return $result\n"
- " }\n"
- " foreach file $list {\n"
- " if {[file tail $file] in {. ..}} {\n"
- " continue\n"
- " }\n"
- " lappend result $file {*}[Find $file]\n"
- " }\n"
- " return $result\n"
- "}\n"
- "proc ::tcl::zipfs::find {directoryName} {\n"
- " return [lsort [Find $directoryName]]\n"
- "}\n";
-
- /*
- * One-time initialization.
- */
-
- WriteLock();
- if (!ZipFS.initialized) {
- ZipfsSetup();
- }
- Unlock();
-
- if (interp) {
- Tcl_Command ensemble;
- Tcl_Obj *mapObj;
-
- Tcl_EvalEx(interp, findproc, TCL_INDEX_NONE, TCL_EVAL_GLOBAL);
- if (!Tcl_IsSafe(interp)) {
- Tcl_LinkVar(interp, "::tcl::zipfs::wrmax", (char *) &ZipFS.wrmax,
- TCL_LINK_INT);
- Tcl_LinkVar(interp, "::tcl::zipfs::fallbackEntryEncoding",
- (char *) &ZipFS.fallbackEntryEncoding, TCL_LINK_STRING);
- }
- ensemble = TclMakeEnsemble(interp, "zipfs",
- Tcl_IsSafe(interp) ? (initMap + 4) : initMap);
-
- /*
- * Add the [zipfs find] subcommand.
- */
-
- Tcl_GetEnsembleMappingDict(NULL, ensemble, &mapObj);
- Tcl_DictObjPut(NULL, mapObj, Tcl_NewStringObj("find", -1),
- Tcl_NewStringObj("::tcl::zipfs::find", -1));
- Tcl_CreateObjCommand(interp, "::tcl::zipfs::tcl_library_init",
- ZipFSTclLibraryObjCmd, NULL, NULL);
- Tcl_PkgProvide(interp, "tcl::zipfs", "2.0");
- }
- return TCL_OK;
-#else /* !HAVE_ZLIB */
- ZIPFS_ERROR(interp, "no zlib available");
- ZIPFS_ERROR_CODE(interp, "NO_ZLIB");
- return TCL_ERROR;
-#endif /* HAVE_ZLIB */
-}
-
-#ifdef HAVE_ZLIB
-
-#if !defined(STATIC_BUILD)
-static int
-ZipfsAppHookFindTclInit(
- const char *archive)
-{
- Tcl_Obj *vfsInitScript;
- int found;
-
- if (zipfs_literal_tcl_library) {
- return TCL_ERROR;
- }
- if (TclZipfs_Mount(NULL, archive, ZIPFS_ZIP_MOUNT, NULL)) {
- /* Either the file doesn't exist or it is not a zip archive */
- return TCL_ERROR;
- }
-
- TclNewLiteralStringObj(vfsInitScript, ZIPFS_ZIP_MOUNT "/init.tcl");
- Tcl_IncrRefCount(vfsInitScript);
- found = Tcl_FSAccess(vfsInitScript, F_OK);
- Tcl_DecrRefCount(vfsInitScript);
- if (found == 0) {
- zipfs_literal_tcl_library = ZIPFS_ZIP_MOUNT;
- return TCL_OK;
- }
-
- TclNewLiteralStringObj(vfsInitScript,
- ZIPFS_ZIP_MOUNT "/tcl_library/init.tcl");
- Tcl_IncrRefCount(vfsInitScript);
- found = Tcl_FSAccess(vfsInitScript, F_OK);
- Tcl_DecrRefCount(vfsInitScript);
- if (found == 0) {
- zipfs_literal_tcl_library = ZIPFS_ZIP_MOUNT "/tcl_library";
- return TCL_OK;
- }
-
- return TCL_ERROR;
-}
-#endif
-
-/*
- *------------------------------------------------------------------------
- *
- * TclZipfsFinalize --
- *
- * Frees all zipfs resources IRRESPECTIVE of open channels (there should
- * not be any!) etc. To be called at process exit time (from
- * Tcl_Finalize->TclFinalizeFilesystem)
- *
- * Results:
- * None.
- *
- * Side effects:
- * Frees up archives loaded into memory.
- *
- *------------------------------------------------------------------------
- */
-void TclZipfsFinalize(void)
-{
- WriteLock();
- if (!ZipFS.initialized) {
- Unlock();
- return;
- }
-
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch zipSearch;
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &zipSearch); hPtr;
- hPtr = Tcl_NextHashEntry(&zipSearch)) {
- ZipFile *zf = (ZipFile *) Tcl_GetHashValue(hPtr);
- Tcl_DeleteHashEntry(hPtr);
- CleanupMount(zf); /* Frees file entries belonging to the archive */
- ZipFSCloseArchive(NULL, zf);
- ckfree(zf);
- }
-
- Tcl_FSUnregister(&zipfsFilesystem);
- Tcl_DeleteHashTable(&ZipFS.fileHash);
- Tcl_DeleteHashTable(&ZipFS.zipHash);
- if (ZipFS.fallbackEntryEncoding) {
- ckfree(ZipFS.fallbackEntryEncoding);
- ZipFS.fallbackEntryEncoding = NULL;
- }
-
- ZipFS.initialized = 0;
- Unlock();
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * TclZipfs_AppHook --
- *
- * Performs the argument munging for the shell
- *
- *-------------------------------------------------------------------------
- */
-
-const char *
-TclZipfs_AppHook(
-#ifdef SUPPORT_BUILTIN_ZIP_INSTALL
- int *argcPtr, /* Pointer to argc */
-#else
- TCL_UNUSED(int *), /*argcPtr*/
-#endif
-#ifdef _WIN32
- TCL_UNUSED(WCHAR ***)) /* argvPtr */
-#else /* !_WIN32 */
- char ***argvPtr) /* Pointer to argv */
-#endif /* _WIN32 */
-{
- const char *archive;
- const char *version = Tcl_InitSubsystems();
-
-#ifdef _WIN32
- Tcl_FindExecutable(NULL);
-#else
- Tcl_FindExecutable((*argvPtr)[0]);
-#endif
- archive = Tcl_GetNameOfExecutable();
- TclZipfs_Init(NULL);
-
- /*
- * Look for init.tcl in one of the locations mounted later in this
- * function.
- */
-
- if (!TclZipfs_Mount(NULL, archive, ZIPFS_APP_MOUNT, NULL)) {
- int found;
- Tcl_Obj *vfsInitScript;
-
- TclNewLiteralStringObj(vfsInitScript, ZIPFS_APP_MOUNT "/main.tcl");
- Tcl_IncrRefCount(vfsInitScript);
- if (Tcl_FSAccess(vfsInitScript, F_OK) == 0) {
- /*
- * Startup script should be set before calling Tcl_AppInit
- */
-
- Tcl_SetStartupScript(vfsInitScript, NULL);
- } else {
- Tcl_DecrRefCount(vfsInitScript);
- }
-
- /*
- * Set Tcl Encodings
- */
-
- if (!zipfs_literal_tcl_library) {
- TclNewLiteralStringObj(vfsInitScript,
- ZIPFS_APP_MOUNT "/tcl_library/init.tcl");
- Tcl_IncrRefCount(vfsInitScript);
- found = Tcl_FSAccess(vfsInitScript, F_OK);
- Tcl_DecrRefCount(vfsInitScript);
- if (found == TCL_OK) {
- zipfs_literal_tcl_library = ZIPFS_APP_MOUNT "/tcl_library";
- return version;
- }
- }
-#ifdef SUPPORT_BUILTIN_ZIP_INSTALL
- } else if (*argcPtr > 1) {
- /*
- * If the first argument is "install", run the supplied installer
- * script.
- */
-
-#ifdef _WIN32
- Tcl_DString ds;
-
- Tcl_DStringInit(&ds);
- archive = Tcl_WCharToUtfDString((*argvPtr)[1], TCL_INDEX_NONE, &ds);
-#else /* !_WIN32 */
- archive = (*argvPtr)[1];
-#endif /* _WIN32 */
- if (strcmp(archive, "install") == 0) {
- Tcl_Obj *vfsInitScript;
-
- /*
- * Run this now to ensure the file is present by the time Tcl_Main
- * wants it.
- */
-
- TclZipfs_TclLibrary();
- TclNewLiteralStringObj(vfsInitScript,
- ZIPFS_ZIP_MOUNT "/tcl_library/install.tcl");
- Tcl_IncrRefCount(vfsInitScript);
- if (Tcl_FSAccess(vfsInitScript, F_OK) == 0) {
- Tcl_SetStartupScript(vfsInitScript, NULL);
- }
- return version;
- } else if (!TclZipfs_Mount(NULL, archive, ZIPFS_APP_MOUNT, NULL)) {
- int found;
- Tcl_Obj *vfsInitScript;
-
- TclNewLiteralStringObj(vfsInitScript, ZIPFS_APP_MOUNT "/main.tcl");
- Tcl_IncrRefCount(vfsInitScript);
- if (Tcl_FSAccess(vfsInitScript, F_OK) == 0) {
- /*
- * Startup script should be set before calling Tcl_AppInit
- */
-
- Tcl_SetStartupScript(vfsInitScript, NULL);
- } else {
- Tcl_DecrRefCount(vfsInitScript);
- }
- /* Set Tcl Encodings */
- TclNewLiteralStringObj(vfsInitScript,
- ZIPFS_APP_MOUNT "/tcl_library/init.tcl");
- Tcl_IncrRefCount(vfsInitScript);
- found = Tcl_FSAccess(vfsInitScript, F_OK);
- Tcl_DecrRefCount(vfsInitScript);
- if (found == TCL_OK) {
- zipfs_literal_tcl_library = ZIPFS_APP_MOUNT "/tcl_library";
- return version;
- }
- }
-#ifdef _WIN32
- Tcl_DStringFree(&ds);
-#endif /* _WIN32 */
-#endif /* SUPPORT_BUILTIN_ZIP_INSTALL */
- }
- return version;
-}
-
-#else /* !HAVE_ZLIB */
-
-/*
- *-------------------------------------------------------------------------
- *
- * TclZipfs_Mount, TclZipfs_MountBuffer, TclZipfs_Unmount --
- *
- * Dummy version when no ZLIB support available.
- *
- *-------------------------------------------------------------------------
- */
-
-int
-TclZipfs_Mount(
- Tcl_Interp *interp, /* Current interpreter. */
- TCL_UNUSED(const char *), /* Path to ZIP file to mount. */
- TCL_UNUSED(const char *), /* Mount point path. */
- TCL_UNUSED(const char *)) /* Password for opening the ZIP, or NULL if
- * the ZIP is unprotected. */
-{
- ZIPFS_ERROR(interp, "no zlib available");
- ZIPFS_ERROR_CODE(interp, "NO_ZLIB");
- return TCL_ERROR;
-}
-
-int
-TclZipfs_MountBuffer(
- Tcl_Interp *interp, /* Current interpreter. NULLable. */
- TCL_UNUSED(const void *),
- TCL_UNUSED(size_t),
- TCL_UNUSED(const char *), /* Mount point path. */
- TCL_UNUSED(int))
-{
- ZIPFS_ERROR(interp, "no zlib available");
- ZIPFS_ERROR_CODE(interp, "NO_ZLIB");
- return TCL_ERROR;
-}
-
-int
-TclZipfs_Unmount(
- Tcl_Interp *interp, /* Current interpreter. */
- TCL_UNUSED(const char *)) /* Mount point path. */
-{
- ZIPFS_ERROR(interp, "no zlib available");
- ZIPFS_ERROR_CODE(interp, "NO_ZLIB");
- return TCL_ERROR;
-}
-
-const char *
-TclZipfs_AppHook(
- TCL_UNUSED(int *), /*argcPtr*/
-#ifdef _WIN32
- TCL_UNUSED(WCHAR ***)) /* argvPtr */
-#else /* !_WIN32 */
- TCL_UNUSED(char ***)) /* Pointer to argv */
-#endif /* _WIN32 */
-{
- return NULL;
-}
-
-Tcl_Obj *
-TclZipfs_TclLibrary(void)
-{
- return NULL;
-}
-
-int TclIsZipfsPath (const char *path)
-{
- return 0;
-}
-
-#endif /* !HAVE_ZLIB */
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 4
- * fill-column: 78
- * End:
- */