summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2015-06-21 22:29:11 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2015-06-21 22:29:11 (GMT)
commit39faa6c610b86034420b32d6fbb5151084f4eeb4 (patch)
tree061be7461b7f66bf06c2b0387445d1ffadac9c7f
parentaf46fd5b12c1265e04beeab0871ce9207e86c7a9 (diff)
downloadtcl-39faa6c610b86034420b32d6fbb5151084f4eeb4.zip
tcl-39faa6c610b86034420b32d6fbb5151084f4eeb4.tar.gz
tcl-39faa6c610b86034420b32d6fbb5151084f4eeb4.tar.bz2
Branch for androwish, as help to keep track on which android-specific changes could be included into the core without harm.
-rw-r--r--Android.mk202
-rw-r--r--debian/changelog23
-rw-r--r--debian/compat1
-rw-r--r--debian/control37
-rw-r--r--debian/copyright141
-rwxr-xr-xdebian/rules131
-rw-r--r--debian/sdltcl8.6-dev.dirs2
-rw-r--r--debian/sdltcl8.6-dev.files2
-rw-r--r--debian/sdltcl8.6-doc.files2
-rw-r--r--debian/sdltcl8.6.files18
-rw-r--r--debian/shlibs.local1
-rw-r--r--generic/tclEncoding.c10
-rw-r--r--generic/tclIOUtil.c74
-rw-r--r--generic/tclInt.decls12
-rw-r--r--generic/tclIntDecls.h19
-rw-r--r--generic/tclMain.c146
-rw-r--r--generic/tclPkgConfig.c20
-rw-r--r--generic/tclStubInit.c9
-rw-r--r--generic/zcrypt.h131
-rw-r--r--generic/zipfs.c2867
-rw-r--r--generic/zipfs.h43
-rw-r--r--pkgs/Android.mk1
-rw-r--r--tcl-config.mk60
-rw-r--r--unix/Makefile.in11
-rw-r--r--unix/tclLoadDl.c40
-rw-r--r--unix/tclUnixFCmd.c21
-rw-r--r--unix/tclUnixInit.c5
-rw-r--r--unix/tclUnixPort.h2
-rw-r--r--unix/tclUnixTime.c5
-rw-r--r--win/Makefile.in5
30 files changed, 4027 insertions, 14 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..9d8ce27
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,202 @@
+LOCAL_PATH := $(call my-dir)
+
+###########################
+#
+# Tcl shared library
+#
+###########################
+
+include $(CLEAR_VARS)
+
+tcl_path := $(LOCAL_PATH)
+
+include $(tcl_path)/tcl-config.mk
+
+LOCAL_MODULE := tcl
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_C_INCLUDES := $(tcl_includes) $(LOCAL_PATH)/libtommath
+
+LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
+
+LOCAL_SRC_FILES := \
+ libtommath/bncore.c \
+ libtommath/bn_reverse.c \
+ libtommath/bn_fast_s_mp_mul_digs.c \
+ libtommath/bn_fast_s_mp_sqr.c \
+ libtommath/bn_mp_add.c \
+ libtommath/bn_mp_add_d.c \
+ libtommath/bn_mp_and.c \
+ libtommath/bn_mp_clamp.c \
+ libtommath/bn_mp_clear.c \
+ libtommath/bn_mp_clear_multi.c \
+ libtommath/bn_mp_cmp.c \
+ libtommath/bn_mp_cmp_d.c \
+ libtommath/bn_mp_cmp_mag.c \
+ libtommath/bn_mp_copy.c \
+ libtommath/bn_mp_cnt_lsb.c \
+ libtommath/bn_mp_count_bits.c \
+ libtommath/bn_mp_div.c \
+ libtommath/bn_mp_div_d.c \
+ libtommath/bn_mp_div_2.c \
+ libtommath/bn_mp_div_2d.c \
+ libtommath/bn_mp_div_3.c \
+ libtommath/bn_mp_exch.c \
+ libtommath/bn_mp_expt_d.c \
+ libtommath/bn_mp_grow.c \
+ libtommath/bn_mp_init.c \
+ libtommath/bn_mp_init_copy.c \
+ libtommath/bn_mp_init_multi.c \
+ libtommath/bn_mp_init_set.c \
+ libtommath/bn_mp_init_set_int.c \
+ libtommath/bn_mp_init_size.c \
+ libtommath/bn_mp_karatsuba_mul.c \
+ libtommath/bn_mp_karatsuba_sqr.c \
+ libtommath/bn_mp_lshd.c \
+ libtommath/bn_mp_mod.c \
+ libtommath/bn_mp_mod_2d.c \
+ libtommath/bn_mp_mul.c \
+ libtommath/bn_mp_mul_2.c \
+ libtommath/bn_mp_mul_2d.c \
+ libtommath/bn_mp_mul_d.c \
+ libtommath/bn_mp_neg.c \
+ libtommath/bn_mp_or.c \
+ libtommath/bn_mp_radix_size.c \
+ libtommath/bn_mp_radix_smap.c \
+ libtommath/bn_mp_read_radix.c \
+ libtommath/bn_mp_rshd.c \
+ libtommath/bn_mp_set.c \
+ libtommath/bn_mp_set_int.c \
+ libtommath/bn_mp_shrink.c \
+ libtommath/bn_mp_sqr.c \
+ libtommath/bn_mp_sqrt.c \
+ libtommath/bn_mp_sub.c \
+ libtommath/bn_mp_sub_d.c \
+ libtommath/bn_mp_to_unsigned_bin.c \
+ libtommath/bn_mp_to_unsigned_bin_n.c \
+ libtommath/bn_mp_toom_mul.c \
+ libtommath/bn_mp_toom_sqr.c \
+ libtommath/bn_mp_toradix_n.c \
+ libtommath/bn_mp_unsigned_bin_size.c \
+ libtommath/bn_mp_xor.c \
+ libtommath/bn_mp_zero.c \
+ libtommath/bn_s_mp_add.c \
+ libtommath/bn_s_mp_mul_digs.c \
+ libtommath/bn_s_mp_sqr.c \
+ libtommath/bn_s_mp_sub.c \
+ generic/regcomp.c \
+ generic/regexec.c \
+ generic/regfree.c \
+ generic/regerror.c \
+ generic/tclAlloc.c \
+ generic/tclAssembly.c \
+ generic/tclAsync.c \
+ generic/tclBasic.c \
+ generic/tclBinary.c \
+ generic/tclCkalloc.c \
+ generic/tclClock.c \
+ generic/tclCmdAH.c \
+ generic/tclCmdIL.c \
+ generic/tclCmdMZ.c \
+ generic/tclCompCmds.c \
+ generic/tclCompCmdsGR.c \
+ generic/tclCompCmdsSZ.c \
+ generic/tclCompExpr.c \
+ generic/tclCompile.c \
+ generic/tclConfig.c \
+ generic/tclDate.c \
+ generic/tclDictObj.c \
+ generic/tclDisassemble.c \
+ generic/tclEncoding.c \
+ generic/tclEnsemble.c \
+ generic/tclEnv.c \
+ generic/tclEvent.c \
+ generic/tclExecute.c \
+ generic/tclFCmd.c \
+ generic/tclFileName.c \
+ generic/tclGet.c \
+ generic/tclHash.c \
+ generic/tclHistory.c \
+ generic/tclIndexObj.c \
+ generic/tclInterp.c \
+ generic/tclIO.c \
+ generic/tclIOCmd.c \
+ generic/tclIOGT.c \
+ generic/tclIOSock.c \
+ generic/tclIOUtil.c \
+ generic/tclIORChan.c \
+ generic/tclIORTrans.c \
+ generic/tclLink.c \
+ generic/tclListObj.c \
+ generic/tclLiteral.c \
+ generic/tclLoad.c \
+ generic/tclMain.c \
+ generic/tclNamesp.c \
+ generic/tclNotify.c \
+ generic/tclObj.c \
+ generic/tclOptimize.c \
+ generic/tclPanic.c \
+ generic/tclParse.c \
+ generic/tclPathObj.c \
+ generic/tclPipe.c \
+ generic/tclPkg.c \
+ generic/tclPkgConfig.c \
+ generic/tclPosixStr.c \
+ generic/tclPreserve.c \
+ generic/tclProc.c \
+ generic/tclRegexp.c \
+ generic/tclResolve.c \
+ generic/tclResult.c \
+ generic/tclScan.c \
+ generic/tclStubInit.c \
+ generic/tclStringObj.c \
+ generic/tclStrToD.c \
+ generic/tclThread.c \
+ generic/tclThreadAlloc.c \
+ generic/tclThreadJoin.c \
+ generic/tclThreadStorage.c \
+ generic/tclTimer.c \
+ generic/tclTomMathInterface.c \
+ generic/tclTrace.c \
+ generic/tclUtil.c \
+ generic/tclUtf.c \
+ generic/tclVar.c \
+ generic/tclZlib.c \
+ generic/tclOO.c \
+ generic/tclOOBasic.c \
+ generic/tclOOCall.c \
+ generic/tclOODefineCmds.c \
+ generic/tclOOInfo.c \
+ generic/tclOOMethod.c \
+ generic/tclOOStubInit.c \
+ generic/tclStubLib.c \
+ generic/tclTomMathStubLib.c \
+ generic/tclOOStubLib.c \
+ generic/zipfs.c \
+ unix/tclAppInit.c \
+ unix/tclLoadDl.c \
+ unix/tclUnixChan.c \
+ unix/tclUnixCompat.c \
+ unix/tclUnixEvent.c \
+ unix/tclUnixFCmd.c \
+ unix/tclUnixFile.c \
+ unix/tclUnixInit.c \
+ unix/tclUnixNotfy.c \
+ unix/tclUnixPipe.c \
+ unix/tclUnixSock.c \
+ unix/tclUnixTest.c \
+ unix/tclUnixThrd.c \
+ unix/tclUnixTime.c
+
+LOCAL_CFLAGS := $(tcl_cflags) \
+ -DPACKAGE_NAME="\"tcl\"" \
+ -DPACKAGE_VERSION="\"8.6\"" \
+ -DBUILD_tcl=1 \
+ -Dmain=tclsh \
+ -O2
+
+LOCAL_LDLIBS := -ldl -lz -llog
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..caad3ba
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,23 @@
+sdltcl8.6 (8.6.4-1) unstable; urgency=low
+
+ * Update to 8.6.4
+
+ -- Christian Werner <chw@ch-werner.de> Thu, 12 Mar 2015 22:00:00 +0100
+
+sdltcl8.6 (8.6.3-1) unstable; urgency=low
+
+ * Update to 8.6.3
+
+ -- Christian Werner <chw@ch-werner.de> Wed, 12 Nov 2014 20:00:00 +0100
+
+sdltcl8.6 (8.6.2-1) unstable; urgency=low
+
+ * Update to 8.6.2
+
+ -- Christian Werner <chw@ch-werner.de> Thu, 28 Aug 2014 07:10:10 +0200
+
+sdltcl8.6 (8.6.1-1) unstable; urgency=low
+
+ * Initial packaging
+
+ -- Christian Werner <chw@ch-werner.de> Sat, 05 Apr 2014 14:44:48 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..3434297
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,37 @@
+Source: sdltcl8.6
+Section: libs
+Priority: optional
+Maintainer: <chw@ch-werner.de>
+Build-Depends: debhelper (>= 5.0.0), quilt
+Standards-Version: 3.8.3
+Homepage: http://www.tcl.tk/
+
+Package: sdltcl8.6
+Section: interpreters
+Priority: optional
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: Tcl (the Tool Command Language) v8.6 - run-time files
+ Tcl is a powerful, easy to use, embeddable, cross-platform interpreted
+ scripting language. This package contains everything you need to run
+ Tcl scripts and Tcl-enabled apps. This version includes thread support.
+
+Package: sdltcl8.6-doc
+Section: doc
+Priority: optional
+Architecture: all
+Suggests: sdltcl8.6
+Description: Tcl (the Tool Command Language) v8.6 - manual pages
+ Tcl is a powerful, easy-to-use, embeddable, cross-platform interpreted
+ scripting language. This package contains the man pages for Tcl commands.
+
+Package: sdltcl8.6-dev
+Section: devel
+Priority: optional
+Architecture: any
+Depends: sdltcl8.6 (= ${binary:Version})
+Suggests: sdltcl8.6-doc
+Description: Tcl (the Tool Command Language) v8.6 - development files
+ Tcl is a powerful, easy-to-use, embeddable, cross-platform interpreted
+ scripting language. This package contains the headers and libraries
+ needed to embed or extend Tcl.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..075c312
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,141 @@
+This package was originally debianized by David Engel <david@debiang.org>
+from sources obtained at http://prdownloads.sourceforge.net/tcl
+
+List of copyright holders mentioned in individual files:
+
+Copyright 1983, 1988-1994 The Regents of the University of California
+Copyright 1991-1999 Karl Lehenbauer and Mark Diekhans
+Copyright 1992-1996 Free Software Foundation, Inc.
+Copyright 1993-1994 Lockheed Missle & Space Company, AI Center
+Copyright 1993-1997 Bell Labs Innovations for Lucent Technologies
+Copyright 1993-1997 Lucent Technologies
+Copyright 1994-1998 Sun Microsystems, Inc.
+Copyright 1995 General Electric Company
+Copyright 1995 Dave Nebinger
+Copyright 1995-1997 Roger E. Critchlow Jr
+Copyright 1996 Lucent Technologies and Jim Ingham
+Copyright 1997-2000 Ajuba Solutions
+Copyright 1998-2000 Scriptics Corporation
+Copyright 1998-1999 Henry Spencer
+Copyright 1998 Paul Duffin
+Copyright 1998 Mark Harrison
+Copyright 1999 America Online, Inc.
+Copyright 1999-2000 Andreas Kupries
+Copyright 2000-2001 ActiveState Corporation, et al
+Copyright 2001 ActiveState Tool Corp.
+Copyright 2001-2002 Apple Computer, Inc.
+Copyright 2001-2002 ActiveState Corporation
+Copyright 2001-2002 Vincent Darley
+Copyright 2001-2002 Donal K. Fellows
+Copyright 2001-2003 Kevin B. Kenny
+Copyright 2001-2002 David Gravereaux
+Contributions from Don Porter, NIST, 2002-2003. (not subject to US copyright)
+Copyright 2005 Tcl Core Team
+Copyright 2005 Daniel A. Steffen
+
+Copyright:
+
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., Scriptics Corporation,
+and other parties. The following terms apply to all files associated
+with the software unless explicitly disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+GOVERNMENT USE: If you are acquiring this software on behalf of the
+U.S. government, the Government shall have only "Restricted Rights"
+in the software and related documentation as defined in the Federal
+Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+are acquiring the software on behalf of the Department of Defense, the
+software shall be classified as "Commercial Computer Software" and the
+Government shall have only "Restricted Rights" as defined in Clause
+252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
+authors grant the U.S. Government and others acting in its behalf
+permission to use and distribute the software in accordance with the
+terms specified in this license.
+
+Several files are distributed under other conditions:
+
+compat/strftime.c:
+/*
+ * strftime.c --
+ *
+ * This file contains a modified version of the BSD 4.4 strftime
+ * function.
+ *
+ * This file is a modified version of the strftime.c file from the BSD 4.4
+ * source. See the copyright notice below for details on redistribution
+ * restrictions. The "license.terms" file does not apply to this file.
+ *
+ * Changes 2002 Copyright (c) 2002 ActiveState Corporation.
+ *
+ * RCS: @(#) $Id: strftime.c,v 1.10.2.3 2005/11/04 18:18:04 kennykb Exp $
+ */
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+compat/dlfcn.h and unix/tclLoadAix.c:
+ * This file is subject to the following copyright notice, which is
+ * different from the notice used elsewhere in Tcl but rougly
+ * equivalent in meaning.
+ *
+ * Copyright (c) 1992,1993,1995,1996, Jens-Uwe Mager, Helios Software GmbH
+ * Not derived from licensed software.
+ *
+ * Permission is granted to freely use, copy, modify, and redistribute
+ * this software, provided that the author is not construed to be liable
+ * for any results of using the software, alterations are clearly marked
+ * as such, and this notice is not modified.
+
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..7a0214c
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,131 @@
+#!/usr/bin/make -f
+# debian/rules that uses debhelper.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+DEB_HOST_GNU_TYPE := $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+export QUILT_PATCHES := debian/patches
+
+v = 8.6
+
+ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+CFLAGS=-g -O0
+else
+# See bug #446335
+CFLAGS=-g -O2 -fno-unit-at-a-time
+endif
+
+CFLAGS+=-DZIPFS_IN_TCL=1
+
+unpatch:
+ dh_testdir
+ quilt pop -a || test $$? = 2
+ rm -rf patch-stamp .pc
+
+patch: patch-stamp
+patch-stamp:
+ dh_testdir
+ quilt push -a || test $$? = 2
+ touch patch-stamp
+
+build: build-stamp
+build-stamp: patch-stamp
+ dh_testdir
+# So so ugly but it works...
+ touch generic/tclStubInit.c
+ cd unix && \
+ CFLAGS="$(CFLAGS)" \
+ ac_cv_func_strtod=yes \
+ tcl_cv_strtod_buggy=1 \
+ ./configure --host=$(DEB_HOST_GNU_TYPE) \
+ --build=$(DEB_BUILD_GNU_TYPE) \
+ --prefix=/opt/sdltk86 \
+ --includedir=/opt/sdltk86/include \
+ --enable-shared \
+ --mandir=/opt/sdltk86/man \
+ --enable-man-symlinks \
+ --enable-man-compression=gzip \
+ --enable-threads \
+ --without-tzdata && \
+ touch ../generic/tclStubInit.c && \
+ $(MAKE)
+# Build the static library.
+ cd unix && \
+ ar cr libtcl$(v).a *.o && \
+ ar d libtcl$(v).a tclAppInit.o && \
+ ranlib libtcl$(v).a
+ touch build-stamp
+
+clean: clean-patched unpatch
+ dh_testdir
+ dh_testroot
+ dh_clean
+
+clean-patched:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp install-stamp
+ cd unix && [ ! -f Makefile ] || $(MAKE) distclean
+# Remove forgotten files
+ rm -f tests/pkg/pkga.so unix/config.log unix/Tcltest.so
+
+install: install-stamp
+install-stamp: build-stamp
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+ cd unix && \
+ GZIP=-9 \
+ $(MAKE) INSTALL_ROOT=`pwd`/../debian/tmp \
+ MAN_INSTALL_DIR=`pwd`/../debian/tmp/opt/sdltk86/man \
+ install install-private-headers install-packages
+# Fix up the libraries.
+ cp unix/libtcl$(v).a debian/tmp/opt/sdltk86/lib
+ touch install-stamp
+
+# Build architecture-independent files here.
+binary-indep: build install
+ dh_testdir -i
+ dh_testroot -i
+ dh_movefiles -i
+ dh_installdocs -i
+ dh_installchangelogs -i ChangeLog
+ dh_compress -i
+ dh_fixperms -i
+ dh_installdeb -i
+ dh_gencontrol -i
+ dh_md5sums -i
+ dh_builddeb -i
+
+# Build architecture-dependent files here.
+binary-arch: build install
+ dh_testdir -a
+ dh_testroot -a
+ dh_movefiles -a
+# now, fix up file locations for .sh
+ mv debian/sdltcl$(v)/opt/sdltk86/lib/tclConfig.sh \
+ debian/sdltcl$(v)-dev/opt/sdltk86/lib
+ dh_installdocs -a
+ dh_installmenu -a
+ dh_installchangelogs -a ChangeLog
+ dh_fixperms -a
+ dh_strip -a
+ dh_compress -a
+ dh_makeshlibs -a -V 'sdltcl$(v) (>= 8.6.2)' -XTcltest
+ dh_installdeb -a
+ dh_shlibdeps -a -ldebian/sdltcl$(v)/opt/sdltk86/lib
+ dh_gencontrol -a
+ dh_md5sums -a
+ dh_builddeb -a
+
+source diff:
+ @echo >&2 'source and diff are obsolete - use dpkg-source -b'; false
+
+binary: binary-indep binary-arch
+
+.PHONY: patch unpatch clean-patched build clean binary-indep binary-arch binary install
+
diff --git a/debian/sdltcl8.6-dev.dirs b/debian/sdltcl8.6-dev.dirs
new file mode 100644
index 0000000..4de4819
--- /dev/null
+++ b/debian/sdltcl8.6-dev.dirs
@@ -0,0 +1,2 @@
+opt/sdltk86/lib
+opt/sdltk86/include
diff --git a/debian/sdltcl8.6-dev.files b/debian/sdltcl8.6-dev.files
new file mode 100644
index 0000000..5cd0878
--- /dev/null
+++ b/debian/sdltcl8.6-dev.files
@@ -0,0 +1,2 @@
+opt/sdltk86/include
+opt/sdltk86/lib/*.a
diff --git a/debian/sdltcl8.6-doc.files b/debian/sdltcl8.6-doc.files
new file mode 100644
index 0000000..56ca7e7
--- /dev/null
+++ b/debian/sdltcl8.6-doc.files
@@ -0,0 +1,2 @@
+opt/sdltk86/man/man3
+opt/sdltk86/man/mann
diff --git a/debian/sdltcl8.6.files b/debian/sdltcl8.6.files
new file mode 100644
index 0000000..501d10a
--- /dev/null
+++ b/debian/sdltcl8.6.files
@@ -0,0 +1,18 @@
+opt/sdltk86/bin
+opt/sdltk86/lib/tcl8
+opt/sdltk86/lib/tcl8/*
+opt/sdltk86/lib/tcl8.6
+opt/sdltk86/lib/tcl8.6/*
+opt/sdltk86/lib/*.so
+opt/sdltk86/lib/*.sh
+opt/sdltk86/lib/itcl*
+opt/sdltk86/lib/itcl*/*
+opt/sdltk86/lib/pkgconfig
+opt/sdltk86/lib/pkgconfig/*
+opt/sdltk86/lib/sqlite*
+opt/sdltk86/lib/sqlite*/*
+opt/sdltk86/lib/tdbc*
+opt/sdltk86/lib/tdbc*/*
+opt/sdltk86/lib/thread*
+opt/sdltk86/lib/thread*/*
+opt/sdltk86/man/man1
diff --git a/debian/shlibs.local b/debian/shlibs.local
new file mode 100644
index 0000000..7da5dd4
--- /dev/null
+++ b/debian/shlibs.local
@@ -0,0 +1 @@
+libtcl8.6 1
diff --git a/generic/tclEncoding.c b/generic/tclEncoding.c
index a7ef199..35caf11 100644
--- a/generic/tclEncoding.c
+++ b/generic/tclEncoding.c
@@ -496,7 +496,11 @@ FillEncodingFileMap(void)
Tcl_Obj *directory, *matchFileList = Tcl_NewObj();
Tcl_Obj **filev;
Tcl_GlobTypeData readableFiles = {
+#ifdef ZIPFS_IN_TCL
+ TCL_GLOB_TYPE_FILE | TCL_GLOB_TYPE_DIR, TCL_GLOB_PERM_R, NULL, NULL
+#else
TCL_GLOB_TYPE_FILE, TCL_GLOB_PERM_R, NULL, NULL
+#endif
};
Tcl_ListObjIndex(NULL, searchPath, i, &directory);
@@ -508,7 +512,13 @@ FillEncodingFileMap(void)
Tcl_ListObjGetElements(NULL, matchFileList, &numFiles, &filev);
for (j=0; j<numFiles; j++) {
Tcl_Obj *encodingName, *fileObj;
+#ifdef ZIPFS_IN_TCL
+ Tcl_StatBuf stat;
+ if ((0 != Tcl_FSStat(filev[j], &stat)) || !S_ISREG(stat.st_mode)) {
+ continue;
+ }
+#endif
fileObj = TclPathPart(NULL, filev[j], TCL_PATH_TAIL);
encodingName = TclPathPart(NULL, fileObj, TCL_PATH_ROOT);
Tcl_DictObjPut(NULL, map, encodingName, directory);
diff --git a/generic/tclIOUtil.c b/generic/tclIOUtil.c
index d2919fc..e6da19d 100644
--- a/generic/tclIOUtil.c
+++ b/generic/tclIOUtil.c
@@ -191,6 +191,10 @@ const Tcl_Filesystem tclNativeFilesystem = {
TclpObjChdir
};
+#ifdef ZIPFS_IN_TCL
+extern Tcl_Filesystem zipfsFilesystem;
+#endif
+
/*
* Define the tail of the linked list. Note that for unconventional uses of
* Tcl without a native filesystem, we may in the future wish to modify the
@@ -790,7 +794,9 @@ TclFinalizeFilesystem(void)
}
fsRecPtr = tmpFsRecPtr;
}
- theFilesystemEpoch++;
+ if (++theFilesystemEpoch == 0) {
+ theFilesystemEpoch = 1;
+ }
filesystemList = NULL;
/*
@@ -823,7 +829,9 @@ void
TclResetFilesystem(void)
{
filesystemList = &nativeFilesystemRecord;
- theFilesystemEpoch++;
+ if (++theFilesystemEpoch == 0) {
+ theFilesystemEpoch = 1;
+ }
#ifdef _WIN32
/*
@@ -908,7 +916,9 @@ Tcl_FSRegister(
* conceivably now belong to different filesystems.
*/
- theFilesystemEpoch++;
+ if (++theFilesystemEpoch == 0) {
+ theFilesystemEpoch = 1;
+ }
Tcl_MutexUnlock(&filesystemMutex);
return TCL_OK;
@@ -973,7 +983,9 @@ Tcl_FSUnregister(
* (which would of course lead to memory exceptions).
*/
- theFilesystemEpoch++;
+ if (++theFilesystemEpoch == 0) {
+ theFilesystemEpoch = 1;
+ }
ckfree(fsRecPtr);
@@ -1304,7 +1316,9 @@ Tcl_FSMountsChanged(
*/
Tcl_MutexLock(&filesystemMutex);
- theFilesystemEpoch++;
+ if (++theFilesystemEpoch == 0) {
+ theFilesystemEpoch = 1;
+ }
Tcl_MutexUnlock(&filesystemMutex);
}
@@ -1399,6 +1413,24 @@ TclFSNormalizeToUniquePath(
Claim();
for (fsRecPtr=firstFsRecPtr; fsRecPtr!=NULL; fsRecPtr=fsRecPtr->nextPtr) {
+#ifdef ZIPFS_IN_TCL
+ if (fsRecPtr->fsPtr == &zipfsFilesystem) {
+ ClientData clientData = NULL;
+ /*
+ * Allow mounted zipfs filesystem to overtake entire normalisation.
+ * This is needed on unix for mounts on symlinks right below root.
+ */
+
+ if (fsRecPtr->fsPtr->pathInFilesystemProc != NULL) {
+ if (fsRecPtr->fsPtr->pathInFilesystemProc(pathPtr,
+ &clientData)!=-1) {
+ TclFSSetPathDetails(pathPtr, fsRecPtr->fsPtr, clientData);
+ break;
+ }
+ }
+ continue;
+ }
+#endif
if (fsRecPtr->fsPtr != &tclNativeFilesystem) {
continue;
}
@@ -1423,6 +1455,11 @@ TclFSNormalizeToUniquePath(
if (fsRecPtr->fsPtr == &tclNativeFilesystem) {
continue;
}
+#ifdef ZIPFS_IN_TCL
+ if (fsRecPtr->fsPtr == &zipfsFilesystem) {
+ continue;
+ }
+#endif
if (fsRecPtr->fsPtr->normalizePathProc != NULL) {
startAt = fsRecPtr->fsPtr->normalizePathProc(interp, pathPtr,
@@ -2890,15 +2927,32 @@ int
Tcl_FSChdir(
Tcl_Obj *pathPtr)
{
- const Tcl_Filesystem *fsPtr;
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
+ const Tcl_Filesystem *fsPtr, *oldFsPtr = NULL;
int retVal = -1;
+ if (tsdPtr->cwdPathPtr != NULL) {
+ oldFsPtr = Tcl_FSGetFileSystemForPath(tsdPtr->cwdPathPtr);
+ }
if (Tcl_FSGetNormalizedPath(NULL, pathPtr) == NULL) {
Tcl_SetErrno(ENOENT);
return retVal;
}
fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
+
+ if ((fsPtr != NULL) && (fsPtr != &tclNativeFilesystem)) {
+ /*
+ * Watch out for tilde substitution.
+ * Only valid in native filesystem.
+ */
+ char *name = Tcl_GetString(pathPtr);
+
+ if ((name != NULL) && (*name == '~')) {
+ fsPtr = &tclNativeFilesystem;
+ }
+ }
+
if (fsPtr != NULL) {
if (fsPtr->chdirProc != NULL) {
/*
@@ -3009,6 +3063,14 @@ Tcl_FSChdir(
} else {
FsUpdateCwd(normDirName, NULL);
}
+
+ /*
+ * If the filesystem changed between old and new cwd
+ * force filesystem refresh on path objects.
+ */
+ if (oldFsPtr != NULL && fsPtr != oldFsPtr) {
+ Tcl_FSMountsChanged(NULL);
+ }
}
return retVal;
diff --git a/generic/tclInt.decls b/generic/tclInt.decls
index 9f7b106..6298708 100644
--- a/generic/tclInt.decls
+++ b/generic/tclInt.decls
@@ -1012,6 +1012,18 @@ declare 251 {
int TclRegisterLiteral(void *envPtr,
char *bytes, int length, int flags)
}
+
+declare 252 {
+ int Tclzipfs_Init(Tcl_Interp *interp)
+}
+declare 253 {
+ int Tclzipfs_Mount(Tcl_Interp *interp, const char *zipname,
+ const char *mntpt, const char *passwd)
+}
+declare 254 {
+ int Tclzipfs_Unmount(Tcl_Interp *interp, const char *zipname)
+}
+
##############################################################################
diff --git a/generic/tclIntDecls.h b/generic/tclIntDecls.h
index f95f999..3e74bbb 100644
--- a/generic/tclIntDecls.h
+++ b/generic/tclIntDecls.h
@@ -617,6 +617,16 @@ EXTERN void TclSetSlaveCancelFlags(Tcl_Interp *interp, int flags,
/* 251 */
EXTERN int TclRegisterLiteral(void *envPtr, char *bytes,
int length, int flags);
+/* 252 */
+EXTERN int Tclzipfs_Init(Tcl_Interp *interp);
+/* 253 */
+EXTERN int Tclzipfs_Mount(Tcl_Interp *interp,
+ const char *zipname, const char *mntpt,
+ const char *passwd);
+/* 254 */
+EXTERN int Tclzipfs_Unmount(Tcl_Interp *interp,
+ const char *zipname);
+
typedef struct TclIntStubs {
int magic;
@@ -874,6 +884,9 @@ typedef struct TclIntStubs {
char * (*tclDoubleDigits) (double dv, int ndigits, int flags, int *decpt, int *signum, char **endPtr); /* 249 */
void (*tclSetSlaveCancelFlags) (Tcl_Interp *interp, int flags, int force); /* 250 */
int (*tclRegisterLiteral) (void *envPtr, char *bytes, int length, int flags); /* 251 */
+ int (*tclzipfs_Init) (Tcl_Interp *interp); /* 252 */
+ int (*tclzipfs_Mount) (Tcl_Interp *interp, const char *zipname, const char *mntpt, const char *passwd); /* 253 */
+ int (*tclzipfs_Unmount) (Tcl_Interp *interp, const char *zipname); /* 254 */
} TclIntStubs;
extern const TclIntStubs *tclIntStubsPtr;
@@ -1305,6 +1318,12 @@ extern const TclIntStubs *tclIntStubsPtr;
(tclIntStubsPtr->tclSetSlaveCancelFlags) /* 250 */
#define TclRegisterLiteral \
(tclIntStubsPtr->tclRegisterLiteral) /* 251 */
+#define Tclzipfs_Init \
+ (tclIntStubsPtr->tclzipfs_Init) /* 252 */
+#define Tclzipfs_Mount \
+ (tclIntStubsPtr->tclzipfs_Mount) /* 253 */
+#define Tclzipfs_Unmount \
+ (tclIntStubsPtr->tclzipfs_Unmount) /* 254 */
#endif /* defined(USE_TCL_STUBS) */
diff --git a/generic/tclMain.c b/generic/tclMain.c
index 360f5e9..2c40c3f 100644
--- a/generic/tclMain.c
+++ b/generic/tclMain.c
@@ -34,6 +34,10 @@
#include "tclInt.h"
+#ifdef ZIPFS_IN_TCL
+#include "zipfs.h"
+#endif
+
/*
* The default prompt used when the user has not overridden it.
*/
@@ -51,6 +55,7 @@
# define TCHAR char
# define TEXT(arg) arg
# define _tcscmp strcmp
+# define _tcsncmp strncmp
#endif
/*
@@ -308,10 +313,16 @@ Tcl_MainEx(
{
Tcl_Obj *path, *resultPtr, *argvPtr, *appName;
const char *encodingName = NULL;
- int code, exitCode = 0;
+ int code, length, exitCode = 0;
Tcl_MainLoopProc *mainLoopProc;
Tcl_Channel chan;
InteractiveState is;
+ const char *zipFile = NULL;
+ Tcl_Obj *zipval = NULL;
+ int autoRun = 1;
+#ifdef ZIPFS_IN_TCL
+ int zipOk = TCL_ERROR;
+#endif
TclpSetInitialEncodings();
TclpFindExecutable((const char *)argv[0]);
@@ -344,6 +355,24 @@ Tcl_MainEx(
Tcl_DecrRefCount(value);
argc -= 3;
argv += 3;
+ } else if (argc > 2) {
+ length = strlen((char *) argv[1]);
+ if ((length >= 2) &&
+ (0 == _tcsncmp(TEXT("-zip"), argv[1], length))) {
+ argc--;
+ argv++;
+ if ((argc > 1) && (argv[1][0] != (TCHAR) '-')) {
+ zipval = NewNativeObj(argv[1], -1);
+ zipFile = Tcl_GetString(zipval);
+ autoRun = 0;
+ argc--;
+ argv++;
+ }
+ } else if ('-' != argv[1][0]) {
+ Tcl_SetStartupScript(NewNativeObj(argv[1], -1), NULL);
+ argc--;
+ argv++;
+ }
} else if ((argc > 1) && ('-' != argv[1][0])) {
Tcl_SetStartupScript(NewNativeObj(argv[1], -1), NULL);
argc--;
@@ -377,6 +406,51 @@ Tcl_MainEx(
Tcl_SetVar2Ex(interp, "tcl_interactive", NULL,
Tcl_NewIntObj(!path && is.tty), TCL_GLOBAL_ONLY);
+#ifdef ZIPFS_IN_TCL
+ zipOk = Tclzipfs_Init(interp);
+ if (zipOk == TCL_OK) {
+ int relax = 0;
+
+ if (zipFile == NULL) {
+ relax = 1;
+#ifdef ANDROID
+ zipFile = getenv("PACKAGE_CODE_PATH");
+ if (zipFile == NULL) {
+ zipFile = Tcl_GetNameOfExecutable();
+ }
+#else
+ zipFile = Tcl_GetNameOfExecutable();
+#endif
+ }
+ if (zipFile != NULL) {
+ zipOk = Tclzipfs_Mount(interp, zipFile, "", NULL);
+ if (!relax && (zipOk != TCL_OK)) {
+ exitCode = 1;
+ goto done;
+ }
+ } else {
+ zipOk = TCL_ERROR;
+ }
+ Tcl_ResetResult(interp);
+ }
+ if (zipOk == TCL_OK) {
+ char *tcl_lib = "/assets/tcl" TCL_VERSION;
+ char *tcl_pkg = "/assets";
+
+ Tcl_SetVar2(interp, "env", "TCL_LIBRARY", tcl_lib, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "tcl_libPath", tcl_lib, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "tcl_library", tcl_lib, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "tcl_pkgPath", tcl_pkg, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "auto_path", tcl_lib,
+ TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT);
+
+ }
+#endif
+ if (zipval != NULL) {
+ Tcl_DecrRefCount(zipval);
+ zipval = NULL;
+ }
+
/*
* Invoke application-specific initialization.
*/
@@ -406,6 +480,76 @@ Tcl_MainEx(
Tcl_CreateExitHandler(FreeMainInterp, interp);
}
+#ifdef ZIPFS_IN_TCL
+ /*
+ * Setup auto loading info to point to mounted ZIP file.
+ */
+
+ if (zipOk == TCL_OK) {
+ char *tcl_lib = "/assets/tcl" TCL_VERSION;
+ char *tcl_pkg = "/assets";
+
+ Tcl_SetVar(interp, "tcl_libPath", tcl_lib, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "tcl_library", tcl_lib, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "tcl_pkgPath", tcl_pkg, TCL_GLOBAL_ONLY);
+
+ /*
+ * We need to re-init encoding (after initializing Tcl),
+ * otherwise "encoding system" will return "identity"
+ */
+
+ TclpSetInitialEncodings();
+ }
+
+ /*
+ * Set embedded application startup file, if any.
+ */
+
+ if ((zipOk == TCL_OK) && autoRun) {
+ char *filename;
+ Tcl_Channel chan;
+
+ filename = "/assets/app/main.tcl";
+ chan = Tcl_OpenFileChannel(NULL, filename, "r", 0);
+ if (chan != (Tcl_Channel) NULL) {
+ Tcl_Obj *arg;
+
+ Tcl_Close(NULL, chan);
+
+ /*
+ * Push back script file to argv, if any.
+ */
+ if ((arg = Tcl_GetStartupScript(NULL)) != NULL) {
+ Tcl_Obj *v, *no;
+
+ no = Tcl_NewStringObj("argv", 4);
+ v = Tcl_ObjGetVar2(interp, no, NULL, TCL_GLOBAL_ONLY);
+ if (v != NULL) {
+ Tcl_Obj **objv, *nv;
+ int objc, i;
+
+ objc = 0;
+ Tcl_ListObjGetElements(NULL, v, &objc, &objv);
+ nv = Tcl_NewListObj(1, &arg);
+ for (i = 0; i < objc; i++) {
+ Tcl_ListObjAppendElement(NULL, nv, objv[i]);
+ }
+ Tcl_IncrRefCount(nv);
+ if (Tcl_ObjSetVar2(interp, no, NULL, nv, TCL_GLOBAL_ONLY)
+ != NULL) {
+ Tcl_GlobalEval(interp, "incr argc");
+ }
+ Tcl_DecrRefCount(nv);
+ }
+ Tcl_DecrRefCount(no);
+ }
+ Tcl_SetStartupScript(Tcl_NewStringObj(filename, -1), NULL);
+ Tcl_SetVar(interp, "argv0", filename, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY);
+ }
+ }
+#endif
+
/*
* Invoke the script specified on the command line, if any. Must fetch it
* again, as the appInitProc might have reset it.
diff --git a/generic/tclPkgConfig.c b/generic/tclPkgConfig.c
index 466d535..3f8178e 100644
--- a/generic/tclPkgConfig.c
+++ b/generic/tclPkgConfig.c
@@ -100,19 +100,35 @@ static Tcl_Config const cfg[] = {
/* Runtime paths to various stuff */
+#ifdef ANDROID
+ {"libdir,runtime", ""},
+ {"bindir,runtime", ""},
+ {"scriptdir,runtime", ""},
+ {"includedir,runtime", ""},
+ {"docdir,runtime", ""},
+#else
{"libdir,runtime", CFG_RUNTIME_LIBDIR},
{"bindir,runtime", CFG_RUNTIME_BINDIR},
{"scriptdir,runtime", CFG_RUNTIME_SCRDIR},
{"includedir,runtime", CFG_RUNTIME_INCDIR},
{"docdir,runtime", CFG_RUNTIME_DOCDIR},
+#endif
/* Installation paths to various stuff */
+#ifdef ANDROID
+ {"libdir,install", ""},
+ {"bindir,install", ""},
+ {"scriptdir,install", ""},
+ {"includedir,install", ""},
+ {"docdir,install", ""},
+#else
{"libdir,install", CFG_INSTALL_LIBDIR},
{"bindir,install", CFG_INSTALL_BINDIR},
{"scriptdir,install", CFG_INSTALL_SCRDIR},
{"includedir,install", CFG_INSTALL_INCDIR},
{"docdir,install", CFG_INSTALL_DOCDIR},
+#endif
/* Last entry, closes the array */
{NULL, NULL}
@@ -123,6 +139,10 @@ TclInitEmbeddedConfigurationInformation(
Tcl_Interp *interp) /* Interpreter the configuration command is
* registered in. */
{
+#if defined(ANDROID) && !defined(TCL_CFGVAL_ENCODING)
+#define TCL_CFGVAL_ENCODING "utf-8"
+#endif
+
Tcl_RegisterConfig(interp, "tcl", cfg, TCL_CFGVAL_ENCODING);
}
diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c
index 7a84cba..d3e0afa 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -560,6 +560,15 @@ static const TclIntStubs tclIntStubs = {
TclDoubleDigits, /* 249 */
TclSetSlaveCancelFlags, /* 250 */
TclRegisterLiteral, /* 251 */
+#ifdef ZIPFS_IN_TCL
+ Tclzipfs_Init, /* 252 */
+ Tclzipfs_Mount, /* 253 */
+ Tclzipfs_Unmount, /* 254 */
+#else
+ 0, /* 252 */
+ 0, /* 253 */
+ 0, /* 254 */
+#endif
};
static const TclIntPlatStubs tclIntPlatStubs = {
diff --git a/generic/zcrypt.h b/generic/zcrypt.h
new file mode 100644
index 0000000..eb9865b
--- /dev/null
+++ b/generic/zcrypt.h
@@ -0,0 +1,131 @@
+/* crypt.h -- base code for crypt/uncrypt ZIPfile
+
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This code is a modified version of crypting code in Infozip distribution
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+
+ If you don't need crypting in your application, just define symbols
+ NOCRYPT and NOUNCRYPT.
+
+ This code support the "Traditional PKWARE Encryption".
+
+ The new AES encryption added on Zip format by Winzip (see the page
+ http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
+ Encryption is not supported.
+*/
+
+#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
+
+/***********************************************************************
+ * Return the next byte in the pseudo-random sequence
+ */
+static int decrypt_byte(unsigned long* pkeys, const unsigned int* pcrc_32_tab)
+{
+ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
+ * unpredictable manner on 16-bit systems; not a problem
+ * with any known compiler so far, though */
+
+ temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
+ return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
+}
+
+/***********************************************************************
+ * Update the encryption keys with the next byte of plain text
+ */
+static int update_keys(unsigned long* pkeys,const unsigned int* pcrc_32_tab,int c)
+{
+ (*(pkeys+0)) = CRC32((*(pkeys+0)), c);
+ (*(pkeys+1)) += (*(pkeys+0)) & 0xff;
+ (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
+ {
+ register int keyshift = (int)((*(pkeys+1)) >> 24);
+ (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
+ }
+ return c;
+}
+
+
+/***********************************************************************
+ * Initialize the encryption keys and the random header according to
+ * the given password.
+ */
+static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned int* pcrc_32_tab)
+{
+ *(pkeys+0) = 305419896L;
+ *(pkeys+1) = 591751049L;
+ *(pkeys+2) = 878082192L;
+ while (*passwd != '\0') {
+ update_keys(pkeys,pcrc_32_tab,(int)*passwd);
+ passwd++;
+ }
+}
+
+#define zdecode(pkeys,pcrc_32_tab,c) \
+ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
+
+#define zencode(pkeys,pcrc_32_tab,c,t) \
+ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
+
+#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
+
+#define RAND_HEAD_LEN 12
+ /* "last resort" source for second part of crypt seed pattern */
+# ifndef ZCR_SEED2
+# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
+# endif
+
+static int crypthead(const char* passwd, /* password string */
+ unsigned char* buf, /* where to write header */
+ int bufSize,
+ unsigned long* pkeys,
+ const unsigned int* pcrc_32_tab,
+ unsigned long crcForCrypting)
+{
+ int n; /* index in random header */
+ int t; /* temporary */
+ int c; /* random byte */
+ unsigned char header[RAND_HEAD_LEN-2]; /* random header */
+ static unsigned calls = 0; /* ensure different random header each time */
+
+ if (bufSize<RAND_HEAD_LEN)
+ return 0;
+
+ /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
+ * output of rand() to get less predictability, since rand() is
+ * often poorly implemented.
+ */
+ if (++calls == 1)
+ {
+ srand((unsigned)(time(NULL) ^ ZCR_SEED2));
+ }
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ c = (rand() >> 7) & 0xff;
+ header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
+ }
+ /* Encrypt random header (last two bytes is high word of crc) */
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
+ }
+ buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
+ buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
+ return n;
+}
+
+#endif
diff --git a/generic/zipfs.c b/generic/zipfs.c
new file mode 100644
index 0000000..ec58d9f
--- /dev/null
+++ b/generic/zipfs.c
@@ -0,0 +1,2867 @@
+#if !defined(_WIN32) && !defined(_WIN64)
+#include <sys/mman.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#ifdef HAVE_ZLIB
+#include "zlib.h"
+#include "zcrypt.h"
+#endif
+#include "tclInt.h"
+#include "tclFileSystem.h"
+#include "zipfs.h"
+
+#ifdef HAVE_ZLIB
+
+#define ZIP_SIG_LEN 4
+
+#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
+
+#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
+
+#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_read_int(p) \
+ ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
+#define zip_read_short(p) \
+ ((p)[0] | ((p)[1] << 8))
+
+#define zip_write_int(p, v) \
+ (p)[0] = (v) & 0xff; (p)[1] = ((v) >> 8) & 0xff; \
+ (p)[2] = ((v) >> 16) & 0xff; (p)[3] = ((v) >> 24) & 0xff;
+#define zip_write_short(p, v) \
+ (p)[0] = (v) & 0xff; (p)[1] = ((v) >> 8) & 0xff;
+
+#if defined(_WIN32) || defined(_WIN64)
+static CONST char alpha[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+#endif
+
+#if !defined(_WIN32) && !defined(_WIN64)
+#ifndef HAVE_LOCALTIME_R
+TCL_DECLARE_MUTEX(localtimeMutex)
+#endif
+#endif
+
+typedef struct ZipFile {
+ char *name; /* Archive name */
+ Tcl_Channel chan; /* Channel handle or NULL */
+ unsigned char *data; /* Memory mapped or malloc'ed file */
+ long length; /* Length of memory mapped file */
+ unsigned char *tofree; /* Non-NULL if malloc'ed file */
+ int nfiles; /* Number of files in archive */
+ int baseoffs; /* Archive start */
+ int baseoffsp; /* Password start */
+ int centoffs; /* Archive directory start */
+ char pwbuf[264]; /* Password buffer */
+#if defined(_WIN32) || defined(_WIN64)
+ HANDLE mh;
+#endif
+ int nopen; /* Number of open files on archive */
+ struct ZipEntry *entries; /* List of files in archive */
+ struct ZipEntry *topents; /* List of top-level dirs in archive */
+ int mntptlen; /* Length of mount point */
+ char mntpt[1]; /* Mount point */
+} ZipFile;
+
+typedef struct ZipEntry {
+ char *name; /* The full pathname of the virtual file */
+ ZipFile *zipfile; /* The ZIP file holding this virtual file */
+ long offset; /* Data offset into memory mapped ZIP file */
+ int nbyte; /* Uncompressed size of the virtual file */
+ int nbytecompr; /* Compressed size of the virtual file */
+ int cmeth; /* Compress method */
+ int isdir; /* Set to 1 if directory */
+ int depth; /* Number of slashes in path. */
+ int crc32; /* CRC-32 */
+ int timestamp; /* Modification time */
+ int isenc; /* True if data is encrypted */
+ 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;
+
+typedef struct ZipChannel {
+ ZipFile *zipfile; /* The ZIP file holding this channel */
+ ZipEntry *zipentry; /* Pointer back to virtual file */
+ unsigned long nmax; /* Max. size for write */
+ unsigned long nbyte; /* Number of bytes of uncompressed data */
+ unsigned long nread; /* Pos of next byte to be read from the channel */
+ unsigned char *ubuf; /* Pointer to the uncompressed data */
+ int iscompr; /* True if data is compressed */
+ int isdir; /* Set to 1 if directory */
+ int isenc; /* True if data is encrypted */
+ int iswr; /* True if open for writing */
+ unsigned long keys[3]; /* Key for decryption */
+} ZipChannel;
+
+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 */
+ Tcl_HashTable fileHash; /* File name to ZipEntry mapping */
+ Tcl_HashTable zipHash; /* Mount to ZipFile mapping */
+} ZipFS = {
+ 0, 0, 0, 0,
+};
+
+/* POSIX like rwlock (multiple reader, single writer) */
+
+TCL_DECLARE_MUTEX(ZipFSMutex)
+static Tcl_Condition ZipFSCond;
+
+static 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 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 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);
+}
+
+static CONST char pwrot[16] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0
+};
+
+static CONST unsigned int crc32tab[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+ 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+ 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+ 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+ 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+ 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+ 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+ 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+ 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+ 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+ 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+ 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+ 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+ 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+ 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+ 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+ 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+ 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+ 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+ 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+ 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+ 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+ 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+ 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+ 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+ 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+ 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+ 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+ 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+ 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+ 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+ 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+ 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+ 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+ 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+ 0x2d02ef8d,
+};
+
+static time_t
+DosTimeDate(int dosDate, int dosTime)
+{
+ time_t now;
+ struct tm *tmp, tm;
+
+ now = time(NULL);
+#if defined(_WIN32) || defined(_WIN64)
+ tmp = localtime(&now);
+ tm = *tmp;
+#else
+#ifdef HAVE_LOCALTIME_R
+ tmp = &tm;
+ localtime_r(&now, tmp);
+#else
+ Tcl_MutexLock(&localtimeMutex);
+ tmp = localtime(&now);
+ tm = *tmp;
+ Tcl_MutexUnlock(&localtimeMutex);
+#endif
+#endif
+ 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 & 0x7e) >> 5;
+ tm.tm_sec = (dosTime & 0x1f) << 1;
+ return mktime(&tm);
+}
+
+static int
+ToDosTime(time_t when)
+{
+ struct tm *tmp, tm;
+
+#if defined(_WIN32) || defined(_WIN64)
+ tmp = localtime(&when);
+ tm = *tmp;
+#else
+#ifdef HAVE_LOCALTIME_R
+ tmp = &tm;
+ localtime_r(&when, tmp);
+#else
+ Tcl_MutexLock(&localtimeMutex);
+ tmp = localtime(&when);
+ tm = *tmp;
+ Tcl_MutexUnlock(&localtimeMutex);
+#endif
+#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 defined(_WIN32) || defined(_WIN64)
+ tmp = localtime(&when);
+ tm = *tmp;
+#else
+#ifdef HAVE_LOCALTIME_R
+ tmp = &tm;
+ localtime_r(&when, tmp);
+#else
+ Tcl_MutexLock(&localtimeMutex);
+ tmp = localtime(&when);
+ tm = *tmp;
+ Tcl_MutexUnlock(&localtimeMutex);
+#endif
+#endif
+ return ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;
+}
+
+static int
+CountSlashes(CONST char *string)
+{
+ int count = 0;
+ CONST char *p = string;
+
+ while (*p != '\0') {
+ if (*p == '/') {
+ count++;
+ }
+ p++;
+ }
+ return count;
+}
+
+static char *
+CanonicalPath(CONST char *root, CONST char *tail, Tcl_DString *dsPtr)
+{
+ char *path;
+ int i, j, c, isunc = 0;
+
+#if defined(_WIN32) || defined(_WIN64)
+ if ((tail[0] != '\0') && (strchr(alpha, tail[0]) != NULL) &&
+ (tail[1] == ':')) {
+ tail += 2;
+ }
+ /* UNC style path */
+ if (tail[0] == '\\') {
+ root = "";
+ ++tail;
+ }
+ if (tail[0] == '\\') {
+ root = "/";
+ ++tail;
+ }
+#endif
+ /* UNC style path */
+ if ((root[0] == '/') && (root[1] == '/')) {
+ isunc = 1;
+ }
+ if (tail[0] == '/') {
+ root = "";
+ ++tail;
+ isunc = 0;
+ }
+ if (tail[0] == '/') {
+ root = "/";
+ ++tail;
+ isunc = 1;
+ }
+ i = strlen(root);
+ j = strlen(tail);
+ Tcl_DStringSetLength(dsPtr, i + j + 1);
+ path = Tcl_DStringValue(dsPtr);
+ memcpy(path, root, i);
+ path[i++] = '/';
+ memcpy(path + i, tail, j);
+#if defined(_WIN32) || defined(_WIN64)
+ for (i = 0; path[i] != '\0'; i++) {
+ if (path[i] == '\\') {
+ path[i] = '/';
+ }
+ }
+#endif
+ for (i = j = 0; (c = path[i]) != '\0'; i++) {
+ if (c == '/') {
+ int c2 = path[i + 1];
+
+ if (c2 == '/') {
+ continue;
+ }
+ if (c2 == '.') {
+ int c3 = path[i + 2];
+
+ if ((c3 == '/') || (c3 == '\0')) {
+ i++;
+ continue;
+ }
+ if ((c3 == '.') &&
+ ((path[i + 3] == '/') || (path [i + 3] == '\0'))) {
+ i += 2;
+ while ((j > 0) && (path[j - 1] != '/')) {
+ j--;
+ }
+ if (j > isunc) {
+ --j;
+ while ((j > 1 + isunc) && (path[j - 2] == '/')) {
+ j--;
+ }
+ }
+ continue;
+ }
+ }
+ }
+ path[j++] = c;
+ }
+ if (j == 0) {
+ path[j++] = '/';
+ }
+ path[j] = 0;
+ Tcl_DStringSetLength(dsPtr, j);
+ return Tcl_DStringValue(dsPtr);
+}
+
+static char *
+AbsolutePath(CONST char *path, Tcl_DString *dsPtr)
+{
+ char *result;
+
+ if (*path == '~') {
+ Tcl_DStringAppend(dsPtr, path, -1);
+ return Tcl_DStringValue(dsPtr);
+ }
+ if ((*path != '/')
+#if defined(_WIN32) || defined(_WIN64)
+ && (*path != '\\') &&
+ (((*path != '\0') && (strchr(alpha, *path) == NULL)) ||
+ (path[1] != ':'))
+#endif
+ ) {
+ Tcl_DString pwd;
+
+ /* relative path */
+ Tcl_DStringInit(&pwd);
+ Tcl_GetCwd(NULL, &pwd);
+ result = Tcl_DStringValue(&pwd);
+#if defined(_WIN32) || defined(_WIN64)
+ if ((result[0] != '\0') && (strchr(alpha, result[0]) != NULL) &&
+ (result[1] == ':')) {
+ result += 2;
+ }
+#endif
+ result = CanonicalPath(result, path, dsPtr);
+ Tcl_DStringFree(&pwd);
+ } else {
+ /* absolute path */
+ result = CanonicalPath("", path, dsPtr);
+ }
+ return result;
+}
+
+static ZipEntry *
+ZipFSLookup(char *filename)
+{
+ char *realname;
+ Tcl_HashEntry *hPtr;
+ ZipEntry *z;
+ Tcl_DString ds;
+
+ Tcl_DStringInit(&ds);
+ realname = AbsolutePath(filename, &ds);
+ hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, realname);
+ z = hPtr ? (ZipEntry *) Tcl_GetHashValue(hPtr) : NULL;
+ Tcl_DStringFree(&ds);
+ return z;
+}
+
+#ifdef NEVER_USED
+static int
+ZipFSLookupMount(char *filename)
+{
+ char *realname;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ ZipFile *zf;
+ Tcl_DString ds;
+ int match = 0;
+
+ Tcl_DStringInit(&ds);
+ realname = AbsolutePath(filename, &ds);
+ hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search);
+ while (hPtr != NULL) {
+ if ((zf = (ZipFile *) Tcl_GetHashValue(hPtr)) != NULL) {
+ if (strcmp(zf->mntpt, realname) == 0) {
+ match = 1;
+ break;
+ }
+ }
+ hPtr = Tcl_NextHashEntry(&search);
+ }
+ Tcl_DStringFree(&ds);
+ return match;
+}
+#endif
+
+static void
+ZipFSCloseArchive(Tcl_Interp *interp, ZipFile *zf)
+{
+#if defined(_WIN32) || defined(_WIN64)
+ if ((zf->data != NULL) && (zf->tofree == NULL)) {
+ UnmapViewOfFile(zf->data);
+ zf->data = NULL;
+ }
+ if (zf->mh != INVALID_HANDLE_VALUE) {
+ CloseHandle(zf->mh);
+ }
+#else
+ if ((zf->data != MAP_FAILED) && (zf->tofree == NULL)) {
+ munmap(zf->data, zf->length);
+ zf->data = MAP_FAILED;
+ }
+#endif
+ if (zf->tofree != NULL) {
+ Tcl_Free((char *) zf->tofree);
+ zf->tofree = NULL;
+ }
+ Tcl_Close(interp, zf->chan);
+ zf->chan = NULL;
+}
+
+static int
+ZipFSOpenArchive(Tcl_Interp *interp, CONST char *zipname, int needZip,
+ ZipFile *zf)
+{
+ int i;
+ ClientData handle;
+ unsigned char *p, *q;
+
+#if defined(_WIN32) || defined(_WIN64)
+ zf->data = NULL;
+ zf->mh = INVALID_HANDLE_VALUE;
+#else
+ zf->data = MAP_FAILED;
+#endif
+ zf->length = 0;
+ zf->nfiles = 0;
+ zf->baseoffs = zf->baseoffsp = 0;
+ zf->tofree = NULL;
+ zf->pwbuf[0] = 0;
+ zf->chan = Tcl_OpenFileChannel(interp, zipname, "r", 0);
+ if (zf->chan == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetChannelHandle(zf->chan, TCL_READABLE, &handle) != TCL_OK) {
+ if (Tcl_SetChannelOption(interp, zf->chan, "-translation", "binary")
+ != TCL_OK) {
+ goto error;
+ }
+ if (Tcl_SetChannelOption(interp, zf->chan, "-encoding", "binary")
+ != TCL_OK) {
+ goto error;
+ }
+ zf->length = Tcl_Seek(zf->chan, 0, SEEK_END);
+ if ((zf->length <= 0) || (zf->length > 64 * 1024 * 1024)) {
+ if (interp) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("illegal file size", -1));
+ }
+ goto error;
+ }
+ Tcl_Seek(zf->chan, 0, SEEK_SET);
+ zf->tofree = zf->data = (unsigned char *) Tcl_Alloc(zf->length);
+ i = Tcl_Read(zf->chan, (char *) zf->data, zf->length);
+ if (i != zf->length) {
+ if (interp) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("file read error", -1));
+ }
+ goto error;
+ }
+ Tcl_Close(interp, zf->chan);
+ zf->chan = NULL;
+ } else {
+#if defined(_WIN32) || defined(_WIN64)
+ zf->length = GetFileSize((HANDLE) handle, 0);
+ if ((zf->length == INVALID_FILE_SIZE) ||
+ (zf->length < ZIP_CENTRAL_END_LEN)) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("invalid file size", -1));
+ }
+ goto error;
+ }
+ zf->mh = CreateFileMapping((HANDLE) handle, 0, PAGE_READONLY, 0,
+ zf->length, 0);
+ if (zf->mh == INVALID_HANDLE_VALUE) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("file mapping failed", -1));
+ }
+ goto error;
+ }
+ zf->data = MapViewOfFile(zf->mh, FILE_MAP_READ, 0, 0, zf->length);
+ if (zf->data == NULL) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("file mapping failed", -1));
+ }
+ goto error;
+ }
+#else
+ zf->length = lseek((int) (long) handle, 0, SEEK_END);
+ if ((zf->length == -1) || (zf->length < ZIP_CENTRAL_END_LEN)) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("invalid file size", -1));
+ }
+ goto error;
+ }
+ lseek((int) (long) handle, 0, SEEK_SET);
+ zf->data = (unsigned char *) mmap(0, zf->length, PROT_READ,
+ MAP_FILE | MAP_PRIVATE,
+ (int) (long) handle, 0);
+ if (zf->data == MAP_FAILED) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("file mapping failed", -1));
+ }
+ goto error;
+ }
+#endif
+ }
+ p = zf->data + zf->length - ZIP_CENTRAL_END_LEN;
+ while (p >= zf->data) {
+ if (*p == (ZIP_CENTRAL_END_SIG & 0xFF)) {
+ if (zip_read_int(p) == ZIP_CENTRAL_END_SIG) {
+ break;
+ }
+ p -= ZIP_SIG_LEN;
+ } else {
+ --p;
+ }
+ }
+ if (p < zf->data) {
+ if (!needZip) {
+ zf->baseoffs = zf->baseoffsp = zf->length;
+ return TCL_OK;
+ }
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("wrong end signature", -1));
+ }
+ goto error;
+ }
+ zf->nfiles = zip_read_short(p + ZIP_CENTRAL_ENTS_OFFS);
+ if (zf->nfiles == 0) {
+ if (!needZip) {
+ zf->baseoffs = zf->baseoffsp = zf->length;
+ return TCL_OK;
+ }
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("empty archive", -1));
+ }
+ goto error;
+ }
+ q = zf->data + zip_read_int(p + ZIP_CENTRAL_DIRSTART_OFFS);
+ p -= zip_read_int(p + ZIP_CENTRAL_DIRSIZE_OFFS);
+ if ((p < zf->data) || (p > (zf->data + zf->length)) ||
+ (q < zf->data) || (q > (zf->data + zf->length))) {
+ if (!needZip) {
+ zf->baseoffs = zf->baseoffsp = zf->length;
+ return TCL_OK;
+ }
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("archive directory not found", -1));
+ }
+ goto error;
+ }
+ zf->baseoffs = zf->baseoffsp = p - q;
+ zf->centoffs = p - zf->data;
+ q = p;
+ for (i = 0; i < zf->nfiles; i++) {
+ int pathlen, comlen, extra;
+
+ if ((q + ZIP_CENTRAL_HEADER_LEN) > (zf->data + zf->length)) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("wrong header length", -1));
+ }
+ goto error;
+ }
+ if (zip_read_int(q) != ZIP_CENTRAL_HEADER_SIG) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("wrong header signature", -1));
+ }
+ goto error;
+ }
+ pathlen = zip_read_short(q + ZIP_CENTRAL_PATHLEN_OFFS);
+ comlen = zip_read_short(q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
+ extra = zip_read_short(q + ZIP_CENTRAL_EXTRALEN_OFFS);
+ q += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN;
+ }
+ q = zf->data + zf->baseoffs;
+ if ((zf->baseoffs >= 6) &&
+ (zip_read_int(q - 4) == ZIP_PASSWORD_END_SIG)) {
+ i = q[-5];
+ if (q - 5 - i > zf->data) {
+ zf->pwbuf[0] = i;
+ memcpy(zf->pwbuf + 1, q - 5 - i, i);
+ zf->baseoffsp -= i ? (5 + i) : 0;
+ }
+ }
+ return TCL_OK;
+
+error:
+ ZipFSCloseArchive(interp, zf);
+ return TCL_ERROR;
+}
+
+int
+Zipfs_Mount(Tcl_Interp *interp, CONST char *zipname, CONST char *mntpt,
+ CONST char *passwd)
+{
+ char *realname;
+ int i, pwlen, isNew;
+ ZipFile *zf, zf0;
+ ZipEntry *z;
+ Tcl_HashEntry *hPtr;
+ Tcl_DString ds, fpBuf;
+ unsigned char *q;
+
+ ReadLock();
+ if (!ZipFS.initialized) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("not initialized", -1));
+ }
+ Unlock();
+ return TCL_ERROR;
+ }
+ if (zipname == NULL) {
+ Tcl_HashSearch search;
+ int ret = TCL_OK;
+
+ i = 0;
+ hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search);
+ while (hPtr != NULL) {
+ if ((zf = (ZipFile *) Tcl_GetHashValue(hPtr)) != NULL) {
+ if (interp != NULL) {
+ Tcl_AppendElement(interp, zf->mntpt);
+ Tcl_AppendElement(interp, zf->name);
+ }
+ ++i;
+ }
+ hPtr = Tcl_NextHashEntry(&search);
+ }
+ if (interp == NULL) {
+ ret = (i > 0) ? TCL_OK : TCL_BREAK;
+ }
+ Unlock();
+ return ret;
+ }
+ if (mntpt == NULL) {
+ if (interp == NULL) {
+ Unlock();
+ return TCL_OK;
+ }
+ Tcl_DStringInit(&ds);
+ hPtr = Tcl_FindHashEntry(&ZipFS.zipHash, AbsolutePath(zipname, &ds));
+ if (hPtr != NULL) {
+ if ((zf = Tcl_GetHashValue(hPtr)) != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(zf->mntpt, -1));
+ }
+ }
+ Unlock();
+ Tcl_DStringFree(&ds);
+ return TCL_OK;
+ }
+ Unlock();
+ pwlen = 0;
+ if (passwd != NULL) {
+ pwlen = strlen(passwd);
+ if ((pwlen > 255) || (strchr(passwd, 0xff) != NULL)) {
+ if (interp) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("illegal password", -1));
+ }
+ return TCL_ERROR;
+ }
+ }
+ if (ZipFSOpenArchive(interp, zipname, 1, &zf0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_DStringInit(&ds);
+ realname = AbsolutePath(zipname, &ds);
+ WriteLock();
+ hPtr = Tcl_CreateHashEntry(&ZipFS.zipHash, realname, &isNew);
+ Tcl_DStringSetLength(&ds, 0);
+ if (!isNew) {
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "already mounted at ", zf->mntpt,
+ (char *) NULL);
+ }
+ goto error;
+ }
+ if (strcmp(mntpt, "/") == 0) {
+ mntpt = "";
+ }
+ zf = (ZipFile *) Tcl_Alloc(sizeof (*zf) + strlen(mntpt) + 1);
+ *zf = zf0;
+ zf->name = Tcl_GetHashKey(&ZipFS.zipHash, hPtr);
+ strcpy(zf->mntpt, mntpt);
+ zf->mntptlen = strlen(zf->mntpt);
+ zf->entries = NULL;
+ zf->topents = NULL;
+ zf->nopen = 0;
+ Tcl_SetHashValue(hPtr, (ClientData) zf);
+ if ((zf->pwbuf[0] == 0) && pwlen) {
+ int k = 0;
+
+ i = pwlen;
+ zf->pwbuf[k++] = i;
+ while (i > 0) {
+ zf->pwbuf[k] = (passwd[i - 1] & 0x0f) |
+ pwrot[(passwd[i - 1] >> 4) & 0x0f];
+ k++;
+ i--;
+ }
+ zf->pwbuf[k] = '\0';
+ }
+ if (mntpt[0] != '\0') {
+ z = (ZipEntry *) Tcl_Alloc(sizeof (*z));
+ z->name = NULL;
+ z->tnext = NULL;
+ z->depth = CountSlashes(mntpt);
+ z->zipfile = zf;
+ z->isdir = 1;
+ z->isenc = 0;
+ z->offset = zf->baseoffs;
+ z->crc32 = 0;
+ z->timestamp = 0;
+ z->nbyte = z->nbytecompr = 0;
+ z->cmeth = ZIP_COMPMETH_STORED;
+ z->data = NULL;
+ hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, mntpt, &isNew);
+ if (!isNew) {
+ /* skip it */
+ Tcl_Free((char *) z);
+ } else {
+ Tcl_SetHashValue(hPtr, (ClientData) z);
+ z->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
+ z->next = zf->entries;
+ zf->entries = z;
+ }
+ }
+ q = zf->data + zf->centoffs;
+ Tcl_DStringInit(&fpBuf);
+ for (i = 0; i < zf->nfiles; i++) {
+ int pathlen, comlen, extra, isdir = 0, dosTime, dosDate, nbcompr, offs;
+ unsigned char *lq, *gq = NULL;
+ char *fullpath, *path;
+
+ pathlen = zip_read_short(q + ZIP_CENTRAL_PATHLEN_OFFS);
+ comlen = zip_read_short(q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
+ extra = zip_read_short(q + ZIP_CENTRAL_EXTRALEN_OFFS);
+ Tcl_DStringSetLength(&ds, 0);
+ Tcl_DStringAppend(&ds, (char *) q + ZIP_CENTRAL_HEADER_LEN, pathlen);
+ path = Tcl_DStringValue(&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->baseoffs +
+ zip_read_int(q + ZIP_CENTRAL_LOCALHDR_OFFS);
+ if ((lq < zf->data) || (lq > (zf->data + zf->length))) {
+ goto nextent;
+ }
+ nbcompr = zip_read_int(lq + ZIP_LOCAL_COMPLEN_OFFS);
+ if (!isdir && (nbcompr == 0) &&
+ (zip_read_int(lq + ZIP_LOCAL_UNCOMPLEN_OFFS) == 0) &&
+ (zip_read_int(lq + ZIP_LOCAL_CRC32_OFFS) == 0)) {
+ gq = q;
+ nbcompr = zip_read_int(gq + ZIP_CENTRAL_COMPLEN_OFFS);
+ }
+ offs = (lq - zf->data)
+ + ZIP_LOCAL_HEADER_LEN
+ + zip_read_short(lq + ZIP_LOCAL_PATHLEN_OFFS)
+ + zip_read_short(lq + ZIP_LOCAL_EXTRALEN_OFFS);
+ if ((offs + nbcompr) > zf->length) {
+ goto nextent;
+ }
+ if (!isdir && (mntpt[0] == '\0') && !CountSlashes(path)) {
+ goto nextent;
+ }
+ Tcl_DStringSetLength(&fpBuf, 0);
+ fullpath = CanonicalPath(mntpt, path, &fpBuf);
+ z = (ZipEntry *) Tcl_Alloc(sizeof (*z));
+ z->name = NULL;
+ z->tnext = NULL;
+ z->depth = CountSlashes(fullpath);
+ z->zipfile = zf;
+ z->isdir = isdir;
+ z->isenc = (zip_read_short(lq + ZIP_LOCAL_FLAGS_OFFS) & 1)
+ && (nbcompr > 12);
+ z->offset = offs;
+ if (gq != NULL) {
+ z->crc32 = zip_read_int(gq + ZIP_CENTRAL_CRC32_OFFS);
+ dosDate = zip_read_short(gq + ZIP_CENTRAL_MDATE_OFFS);
+ dosTime = zip_read_short(gq + ZIP_CENTRAL_MTIME_OFFS);
+ z->timestamp = DosTimeDate(dosDate, dosTime);
+ z->nbyte = zip_read_int(gq + ZIP_CENTRAL_UNCOMPLEN_OFFS);
+ z->cmeth = zip_read_short(gq + ZIP_CENTRAL_COMPMETH_OFFS);
+ } else {
+ z->crc32 = zip_read_int(lq + ZIP_LOCAL_CRC32_OFFS);
+ dosDate = zip_read_short(lq + ZIP_LOCAL_MDATE_OFFS);
+ dosTime = zip_read_short(lq + ZIP_LOCAL_MTIME_OFFS);
+ z->timestamp = DosTimeDate(dosDate, dosTime);
+ z->nbyte = zip_read_int(lq + ZIP_LOCAL_UNCOMPLEN_OFFS);
+ z->cmeth = zip_read_short(lq + ZIP_LOCAL_COMPMETH_OFFS);
+ }
+ z->nbytecompr = nbcompr;
+ z->data = NULL;
+ hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, fullpath, &isNew);
+ if (!isNew) {
+ /* skip it */
+ Tcl_Free((char *) z);
+ } else {
+ Tcl_SetHashValue(hPtr, (ClientData) z);
+ z->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
+ z->next = zf->entries;
+ zf->entries = z;
+ if (isdir && (mntpt[0] == '\0') && (z->depth == 1)) {
+ z->tnext = zf->topents;
+ zf->topents = z;
+ }
+ if (!z->isdir && (z->depth > 1)) {
+ char *dir, *end;
+ ZipEntry *zd;
+
+ Tcl_DStringSetLength(&ds, strlen(z->name) + 8);
+ Tcl_DStringSetLength(&ds, 0);
+ Tcl_DStringAppend(&ds, z->name, -1);
+ dir = Tcl_DStringValue(&ds);
+ end = strrchr(dir, '/');
+ while ((end != NULL) && (end != dir)) {
+ Tcl_DStringSetLength(&ds, end - dir);
+ hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, dir);
+ if (hPtr != NULL) {
+ break;
+ }
+ zd = (ZipEntry *) Tcl_Alloc(sizeof (*zd));
+ zd->name = NULL;
+ zd->tnext = NULL;
+ zd->depth = CountSlashes(dir);
+ zd->zipfile = zf;
+ zd->isdir = 1;
+ zd->isenc = 0;
+ zd->offset = z->offset;
+ zd->crc32 = 0;
+ zd->timestamp = z->timestamp;
+ zd->nbyte = zd->nbytecompr = 0;
+ zd->cmeth = ZIP_COMPMETH_STORED;
+ zd->data = NULL;
+ hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, dir, &isNew);
+ if (!isNew) {
+ /* should never happen but skip it */
+ Tcl_Free((char *) zd);
+ } else {
+ Tcl_SetHashValue(hPtr, (ClientData) zd);
+ zd->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
+ zd->next = zf->entries;
+ zf->entries = zd;
+ if ((mntpt[0] == '\0') && (zd->depth == 1)) {
+ zd->tnext = zf->topents;
+ zf->topents = zd;
+ }
+ }
+ end = strrchr(dir, '/');
+ }
+ }
+ }
+nextent:
+ q += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN;
+ }
+ Tcl_DStringFree(&fpBuf);
+ Tcl_DStringFree(&ds);
+ Unlock();
+ Tcl_FSMountsChanged(NULL);
+ return TCL_OK;
+
+error:
+ Tcl_DStringFree(&ds);
+ Unlock();
+ ZipFSCloseArchive(interp, zf);
+ Tcl_Free((char *) zf);
+ return TCL_ERROR;
+}
+
+int
+Zipfs_Unmount(Tcl_Interp *interp, CONST char *zipname)
+{
+ char *realname;
+ ZipFile *zf;
+ ZipEntry *z, *znext;
+ Tcl_HashEntry *hPtr;
+ Tcl_DString ds;
+ int ret = TCL_OK, unmounted = 0;
+
+ Tcl_DStringInit(&ds);
+ realname = AbsolutePath(zipname, &ds);
+ WriteLock();
+ if (!ZipFS.initialized) {
+ goto done;
+ }
+ hPtr = Tcl_FindHashEntry(&ZipFS.zipHash, realname);
+ if (hPtr == NULL) {
+ /* does not report error */
+ goto done;
+ }
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
+ if (zf->nopen > 0) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("filesystem is busy", -1));
+ }
+ ret = TCL_ERROR;
+ goto done;
+ }
+ Tcl_DeleteHashEntry(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 != NULL) {
+ Tcl_Free((char *) z->data);
+ }
+ Tcl_Free((char *) z);
+ }
+ ZipFSCloseArchive(interp, zf);
+ Tcl_Free((char *) zf);
+ unmounted = 1;
+done:
+ Unlock();
+ Tcl_DStringFree(&ds);
+ if (unmounted) {
+ Tcl_FSMountsChanged(NULL);
+ }
+ return ret;
+}
+
+static int
+ZipFSMountCmd(ClientData clientData, Tcl_Interp *interp,
+ int argc, CONST char **argv)
+{
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ?zipfile ?mountpoint? ?password???\"", 0);
+ return TCL_ERROR;
+ }
+ return Zipfs_Mount(interp, (argc > 1) ? argv[1] : NULL,
+ (argc > 2) ? argv[2] : NULL,
+ (argc > 3) ? argv[3] : NULL);
+}
+
+static int
+ZipFSUnmountCmd(ClientData clientData, Tcl_Interp *interp,
+ int argc, CONST char **argv)
+{
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " zipfile\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return Zipfs_Unmount(interp, argv[1]);
+}
+
+static int
+ZipFSMkKeyCmd(ClientData clientData, Tcl_Interp *interp,
+ int argc, CONST char **argv)
+{
+ int len, i = 0;
+ char pwbuf[264];
+
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " password\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ len = strlen(argv[1]);
+ if (len == 0) {
+ return TCL_OK;
+ }
+ if ((len > 255) || (strchr(argv[1], 0xff) != NULL)) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("illegal password", -1));
+ return TCL_ERROR;
+ }
+ while (len > 0) {
+ int ch = argv[1][len - 1];
+
+ pwbuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
+ i++;
+ len--;
+ }
+ pwbuf[i] = i;
+ ++i;
+ pwbuf[i++] = (char) ZIP_PASSWORD_END_SIG;
+ pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 8);
+ pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 16);
+ pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 24);
+ pwbuf[i] = '\0';
+ Tcl_AppendResult(interp, pwbuf, (char *) NULL);
+ return TCL_OK;
+}
+
+static int
+ZipAddFile(Tcl_Interp *interp, CONST char *path, Tcl_Channel out,
+ CONST char *passwd, char *buf, int bufsize, Tcl_HashTable *fileHash)
+{
+ Tcl_Channel in;
+ Tcl_HashEntry *hPtr;
+ ZipEntry *z;
+ z_stream stream;
+ CONST char *zpath;
+ int nbyte, nbytecompr, len, crc, flush, pos[3], zpathlen, olen;
+ int mtime = 0, isNew, align = 0, cmeth;
+ unsigned long keys[3], keys0[3];
+ char obuf[4096];
+
+ zpath = path;
+ while (zpath != NULL && zpath[0] == '/') {
+ zpath++;
+ }
+ if ((zpath == NULL) || (zpath[0] == '\0')) {
+ return TCL_OK;
+ }
+ zpathlen = strlen(zpath);
+ if (zpathlen + ZIP_CENTRAL_HEADER_LEN > bufsize) {
+ Tcl_AppendResult(interp, "path too long for \"", path, "\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ in = Tcl_OpenFileChannel(interp, path, "r", 0);
+ if ((in == NULL) ||
+ (Tcl_SetChannelOption(interp, in, "-translation", "binary")
+ != TCL_OK) ||
+ (Tcl_SetChannelOption(interp, in, "-encoding", "binary")
+ != TCL_OK)) {
+#if defined(_WIN32) || defined(_WIN64)
+ /* hopefully a directory */
+ if (strcmp("permission denied", Tcl_PosixError(interp)) == 0) {
+ Tcl_Close(interp, in);
+ return TCL_OK;
+ }
+#endif
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ } else {
+ Tcl_Obj *pathObj = Tcl_NewStringObj(path, -1);
+ Tcl_StatBuf statBuf;
+
+ Tcl_IncrRefCount(pathObj);
+ if (Tcl_FSStat(pathObj, &statBuf) != -1) {
+ mtime = statBuf.st_mtime;
+ }
+ Tcl_DecrRefCount(pathObj);
+ }
+ Tcl_ResetResult(interp);
+ crc = 0;
+ nbyte = nbytecompr = 0;
+ while ((len = Tcl_Read(in, buf, bufsize)) > 0) {
+ crc = crc32(crc, (unsigned char *) buf, len);
+ nbyte += len;
+ }
+ if (len == -1) {
+ if (nbyte == 0) {
+ if (strcmp("illegal operation on a directory",
+ Tcl_PosixError(interp)) == 0) {
+ Tcl_Close(interp, in);
+ return TCL_OK;
+ }
+ }
+ Tcl_AppendResult(interp, "read error on \"", path, "\"",
+ (char *) NULL);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ if (Tcl_Seek(in, 0, SEEK_SET) == -1) {
+ Tcl_AppendResult(interp, "seek error on \"", path, "\"",
+ (char *) NULL);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ pos[0] = Tcl_Tell(out);
+ memset(buf, '\0', ZIP_LOCAL_HEADER_LEN);
+ memcpy(buf + ZIP_LOCAL_HEADER_LEN, zpath, zpathlen);
+ len = zpathlen + ZIP_LOCAL_HEADER_LEN;
+ if (Tcl_Write(out, buf, len) != len) {
+wrerr:
+ Tcl_AppendResult(interp, "write error", (char *) NULL);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ if ((len + pos[0]) & 3) {
+ char abuf[8];
+
+ /*
+ * Align payload to next 4-byte boundary using a dummy extra
+ * entry similar to the zipalign tool from Android's SDK.
+ */
+ align = 4 + ((len + pos[0]) & 3);
+ zip_write_short(abuf, 0xffff);
+ zip_write_short(abuf + 2, align - 4);
+ zip_write_int(abuf + 4, 0x03020100);
+ if (Tcl_Write(out, abuf, align) != align) {
+ goto wrerr;
+ }
+ }
+ if (passwd != NULL) {
+ int i, ch, tmp;
+ unsigned char kvbuf[24];
+ Tcl_Obj *ret;
+
+ init_keys(passwd, keys, crc32tab);
+ for (i = 0; i < 12 - 2; i++) {
+ if (Tcl_Eval(interp, "expr int(rand() * 256) % 256") != TCL_OK) {
+ Tcl_AppendResult(interp, "PRNG error", (char *) NULL);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ ret = Tcl_GetObjResult(interp);
+ if (Tcl_GetIntFromObj(interp, ret, &ch) != TCL_OK) {
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ kvbuf[i + 12] = (unsigned char) zencode(keys, crc32tab, ch, tmp);
+ }
+ Tcl_ResetResult(interp);
+ init_keys(passwd, keys, crc32tab);
+ for (i = 0; i < 12 - 2; i++) {
+ kvbuf[i] = (unsigned char) zencode(keys, crc32tab,
+ kvbuf[i + 12], tmp);
+ }
+ kvbuf[i++] = (unsigned char) zencode(keys, crc32tab, crc >> 16, tmp);
+ kvbuf[i++] = (unsigned char) zencode(keys, crc32tab, crc >> 24, tmp);
+ len = Tcl_Write(out, (char *) kvbuf, 12);
+ memset(kvbuf, 0, 24);
+ if (len != 12) {
+ Tcl_AppendResult(interp, "write error", (char *) NULL);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ memcpy(keys0, keys, sizeof (keys0));
+ nbytecompr += 12;
+ }
+ Tcl_Flush(out);
+ pos[2] = Tcl_Tell(out);
+ cmeth = ZIP_COMPMETH_DEFLATED;
+ memset(&stream, 0, sizeof (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_AppendResult(interp, "compression init error on \"", path, "\"",
+ (char *) NULL);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ do {
+ len = Tcl_Read(in, buf, bufsize);
+ if (len == -1) {
+ Tcl_AppendResult(interp, "read error on \"", path, "\"",
+ (char *) NULL);
+ deflateEnd(&stream);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ 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 == Z_STREAM_ERROR) {
+ Tcl_AppendResult(interp, "deflate error on \"", path, "\"",
+ (char *) NULL);
+ deflateEnd(&stream);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ olen = sizeof (obuf) - stream.avail_out;
+ if (passwd != NULL) {
+ int i, tmp;
+
+ for (i = 0; i < olen; i++) {
+ obuf[i] = (char) zencode(keys, crc32tab, obuf[i], tmp);
+ }
+ }
+ if (olen && (Tcl_Write(out, obuf, olen) != olen)) {
+ Tcl_AppendResult(interp, "write error", (char *) NULL);
+ deflateEnd(&stream);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ nbytecompr += olen;
+ } while (stream.avail_out == 0);
+ } while (flush != Z_FINISH);
+ deflateEnd(&stream);
+ Tcl_Flush(out);
+ pos[1] = Tcl_Tell(out);
+ if (nbyte - nbytecompr <= 0) {
+ /*
+ * Compressed file larger than input,
+ * write it again uncompressed.
+ */
+ if ((int) Tcl_Seek(in, 0, SEEK_SET) != 0) {
+ goto seekErr;
+ }
+ if ((int) Tcl_Seek(out, pos[2], SEEK_SET) != pos[2]) {
+seekErr:
+ Tcl_Close(interp, in);
+ Tcl_AppendResult(interp, "seek error", (char *) NULL);
+ return TCL_ERROR;
+ }
+ nbytecompr = (passwd != NULL) ? 12 : 0;
+ while (1) {
+ len = Tcl_Read(in, buf, bufsize);
+ if (len == -1) {
+ Tcl_AppendResult(interp, "read error on \"", path, "\"",
+ (char *) NULL);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ } else if (len == 0) {
+ break;
+ }
+ if (passwd != NULL) {
+ int i, tmp;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (char) zencode(keys0, crc32tab, buf[i], tmp);
+ }
+ }
+ if (Tcl_Write(out, buf, len) != len) {
+ Tcl_AppendResult(interp, "write error", (char *) NULL);
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+ }
+ nbytecompr += len;
+ }
+ cmeth = ZIP_COMPMETH_STORED;
+ Tcl_Flush(out);
+ pos[1] = Tcl_Tell(out);
+ Tcl_TruncateChannel(out, pos[1]);
+ }
+ Tcl_Close(interp, in);
+
+ z = (ZipEntry *) Tcl_Alloc(sizeof (*z));
+ z->name = NULL;
+ z->tnext = NULL;
+ z->depth = 0;
+ z->zipfile = NULL;
+ z->isdir = 0;
+ z->isenc = (passwd != NULL) ? 1 : 0;
+ z->offset = pos[0];
+ z->crc32 = crc;
+ z->timestamp = mtime;
+ z->nbyte = nbyte;
+ z->nbytecompr = nbytecompr;
+ z->cmeth = cmeth;
+ z->data = NULL;
+ hPtr = Tcl_CreateHashEntry(fileHash, zpath, &isNew);
+ if (!isNew) {
+ Tcl_AppendResult(interp, "not unique path name \"", path, "\"",
+ (char *) NULL);
+ Tcl_Free((char *) z);
+ return TCL_ERROR;
+ } else {
+ Tcl_SetHashValue(hPtr, (ClientData) z);
+ z->name = Tcl_GetHashKey(fileHash, hPtr);
+ z->next = NULL;
+ }
+
+ /*
+ * Write final local header information.
+ */
+ zip_write_int(buf + ZIP_LOCAL_SIG_OFFS, ZIP_LOCAL_HEADER_SIG);
+ zip_write_short(buf + ZIP_LOCAL_VERSION_OFFS, ZIP_MIN_VERSION);
+ zip_write_short(buf + ZIP_LOCAL_FLAGS_OFFS, z->isenc);
+ zip_write_short(buf + ZIP_LOCAL_COMPMETH_OFFS, z->cmeth);
+ zip_write_short(buf + ZIP_LOCAL_MTIME_OFFS, ToDosTime(z->timestamp));
+ zip_write_short(buf + ZIP_LOCAL_MDATE_OFFS, ToDosDate(z->timestamp));
+ zip_write_int(buf + ZIP_LOCAL_CRC32_OFFS, z->crc32);
+ zip_write_int(buf + ZIP_LOCAL_COMPLEN_OFFS, z->nbytecompr);
+ zip_write_int(buf + ZIP_LOCAL_UNCOMPLEN_OFFS, z->nbyte);
+ zip_write_short(buf + ZIP_LOCAL_PATHLEN_OFFS, zpathlen);
+ zip_write_short(buf + ZIP_LOCAL_EXTRALEN_OFFS, align);
+ if ((int) Tcl_Seek(out, pos[0], SEEK_SET) != pos[0]) {
+ Tcl_DeleteHashEntry(hPtr);
+ Tcl_Free((char *) z);
+ Tcl_AppendResult(interp, "seek error", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_Write(out, buf, ZIP_LOCAL_HEADER_LEN) != ZIP_LOCAL_HEADER_LEN) {
+ Tcl_DeleteHashEntry(hPtr);
+ Tcl_Free((char *) z);
+ Tcl_AppendResult(interp, "write error", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_Flush(out);
+ if ((int) Tcl_Seek(out, pos[1], SEEK_SET) != pos[1]) {
+ Tcl_DeleteHashEntry(hPtr);
+ Tcl_Free((char *) z);
+ Tcl_AppendResult(interp, "seek error", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+static int
+ZipFSMkZipOrImgCmd(ClientData clientData, Tcl_Interp *interp,
+ int isImg, int argc, CONST char **argv)
+{
+ Tcl_Channel out;
+ int len = 0, pwlen = 0, i, ret = TCL_ERROR, largc, pos[3];
+ CONST char **largv;
+ Tcl_DString ds;
+ ZipEntry *z;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ Tcl_HashTable fileHash;
+ char pwbuf[264], buf[4096];
+
+ if ((argc < 3) || (argc > (isImg ? 5 : 4))) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " outfile indir ?password?",
+ isImg ? " ?infile?\"" : "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ pwbuf[0] = 0;
+ if (argc > 3) {
+ pwlen = strlen(argv[3]);
+ if ((pwlen > 255) || (strchr(argv[1], 0xff) != NULL)) {
+ Tcl_AppendResult(interp, "illegal password", (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ Tcl_DStringInit(&ds);
+ Tcl_DStringAppendElement(&ds, "::zipfs::find");
+ Tcl_DStringAppendElement(&ds, argv[2]);
+ if (Tcl_Eval(interp, Tcl_DStringValue(&ds)) != TCL_OK) {
+ Tcl_DStringFree(&ds);
+ return TCL_ERROR;
+ }
+ Tcl_DStringFree(&ds);
+ if (Tcl_SplitList(interp, Tcl_GetStringResult(interp), &largc, &largv)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_ResetResult(interp);
+ if (largc == 0) {
+ Tcl_Free((char *) largv);
+ Tcl_AppendResult(interp, "empty archive", (char *) NULL);
+ return TCL_ERROR;
+ }
+ out = Tcl_OpenFileChannel(interp, argv[1], "w", 0755);
+ if ((out == NULL) ||
+ (Tcl_SetChannelOption(interp, out, "-translation", "binary")
+ != TCL_OK) ||
+ (Tcl_SetChannelOption(interp, out, "-encoding", "binary")
+ != TCL_OK)) {
+ Tcl_Close(interp, out);
+ Tcl_Free((char *) largv);
+ return TCL_ERROR;
+ }
+ if (isImg) {
+ ZipFile zf0;
+
+ if (ZipFSOpenArchive(interp, (argc > 4) ? argv[4] :
+ Tcl_GetNameOfExecutable(), 0, &zf0) != TCL_OK) {
+ Tcl_Close(interp, out);
+ Tcl_Free((char *) largv);
+ return TCL_ERROR;
+ }
+ if (pwlen && (argc > 3)) {
+ i = 0;
+ len = pwlen;
+ while (len > 0) {
+ int ch = argv[3][len - 1];
+
+ pwbuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
+ i++;
+ len--;
+ }
+ pwbuf[i] = i;
+ ++i;
+ pwbuf[i++] = (char) ZIP_PASSWORD_END_SIG;
+ pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 8);
+ pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 16);
+ pwbuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 24);
+ pwbuf[i] = '\0';
+ }
+ i = Tcl_Write(out, (char *) zf0.data, zf0.baseoffsp);
+ if (i != zf0.baseoffsp) {
+ Tcl_AppendResult(interp, "write error", (char *) NULL);
+ Tcl_Close(interp, out);
+ Tcl_Free((char *) largv);
+ ZipFSCloseArchive(interp, &zf0);
+ return TCL_ERROR;
+ }
+ ZipFSCloseArchive(interp, &zf0);
+ len = strlen(pwbuf);
+ if (len > 0) {
+ i = Tcl_Write(out, pwbuf, len);
+ if (i != len) {
+ Tcl_AppendResult(interp, "write error", (char *) NULL);
+ Tcl_Close(interp, out);
+ Tcl_Free((char *) largv);
+ return TCL_ERROR;
+ }
+ }
+ memset(pwbuf, 0, sizeof (pwbuf));
+ Tcl_Flush(out);
+ }
+ Tcl_InitHashTable(&fileHash, TCL_STRING_KEYS);
+ pos[0] = Tcl_Tell(out);
+ for (i = 0; i < largc; i++) {
+ if (ZipAddFile(interp, largv[i], out, (pwlen > 0) ? argv[3] : NULL,
+ buf, sizeof (buf), &fileHash)
+ != TCL_OK) {
+ goto done;
+ }
+ }
+ pos[1] = Tcl_Tell(out);
+ hPtr = Tcl_FirstHashEntry(&fileHash, &search);
+ i = 0;
+ while (hPtr != NULL) {
+ z = (ZipEntry *) Tcl_GetHashValue(hPtr);
+ len = strlen(z->name);
+ zip_write_int(buf + ZIP_CENTRAL_SIG_OFFS, ZIP_CENTRAL_HEADER_SIG);
+ zip_write_short(buf + ZIP_CENTRAL_VERSIONMADE_OFFS, ZIP_MIN_VERSION);
+ zip_write_short(buf + ZIP_CENTRAL_VERSION_OFFS, ZIP_MIN_VERSION);
+ zip_write_short(buf + ZIP_CENTRAL_FLAGS_OFFS, z->isenc ? 1 : 0);
+ zip_write_short(buf + ZIP_CENTRAL_COMPMETH_OFFS, z->cmeth);
+ zip_write_short(buf + ZIP_CENTRAL_MTIME_OFFS, ToDosTime(z->timestamp));
+ zip_write_short(buf + ZIP_CENTRAL_MDATE_OFFS, ToDosDate(z->timestamp));
+ zip_write_int(buf + ZIP_CENTRAL_CRC32_OFFS, z->crc32);
+ zip_write_int(buf + ZIP_CENTRAL_COMPLEN_OFFS, z->nbytecompr);
+ zip_write_int(buf + ZIP_CENTRAL_UNCOMPLEN_OFFS, z->nbyte);
+ zip_write_short(buf + ZIP_CENTRAL_PATHLEN_OFFS, len);
+ zip_write_short(buf + ZIP_CENTRAL_EXTRALEN_OFFS, 0);
+ zip_write_short(buf + ZIP_CENTRAL_FCOMMENTLEN_OFFS, 0);
+ zip_write_short(buf + ZIP_CENTRAL_DISKFILE_OFFS, 0);
+ zip_write_short(buf + ZIP_CENTRAL_IATTR_OFFS, 0);
+ zip_write_int(buf + ZIP_CENTRAL_EATTR_OFFS, 0);
+ zip_write_int(buf + ZIP_CENTRAL_LOCALHDR_OFFS, z->offset - pos[0]);
+ memcpy(buf + ZIP_CENTRAL_HEADER_LEN, z->name, len);
+ len += ZIP_CENTRAL_HEADER_LEN;
+ if (Tcl_Write(out, buf, len) != len) {
+ Tcl_AppendResult(interp, "write error", (char *) NULL);
+ goto done;
+ }
+ hPtr = Tcl_NextHashEntry(&search);
+ ++i;
+ }
+ Tcl_Flush(out);
+ pos[2] = Tcl_Tell(out);
+ zip_write_int(buf + ZIP_CENTRAL_END_SIG_OFFS, ZIP_CENTRAL_END_SIG);
+ zip_write_short(buf + ZIP_CENTRAL_DISKNO_OFFS, 0);
+ zip_write_short(buf + ZIP_CENTRAL_DISKDIR_OFFS, 0);
+ zip_write_short(buf + ZIP_CENTRAL_ENTS_OFFS, i);
+ zip_write_short(buf + ZIP_CENTRAL_TOTALENTS_OFFS, i);
+ zip_write_int(buf + ZIP_CENTRAL_DIRSIZE_OFFS, pos[2] - pos[1]);
+ zip_write_int(buf + ZIP_CENTRAL_DIRSTART_OFFS, pos[1] - pos[0]);
+ zip_write_short(buf + ZIP_CENTRAL_COMMENTLEN_OFFS, 0);
+ if (Tcl_Write(out, buf, ZIP_CENTRAL_END_LEN) != ZIP_CENTRAL_END_LEN) {
+ Tcl_AppendResult(interp, "write error", (char *) NULL);
+ goto done;
+ }
+ Tcl_Flush(out);
+ ret = TCL_OK;
+done:
+ Tcl_Free((char *) largv);
+ Tcl_Close(interp, out);
+ hPtr = Tcl_FirstHashEntry(&fileHash, &search);
+ while (hPtr != NULL) {
+ z = (ZipEntry *) Tcl_GetHashValue(hPtr);
+ Tcl_Free((char *) z);
+ Tcl_DeleteHashEntry(hPtr);
+ hPtr = Tcl_FirstHashEntry(&fileHash, &search);
+ }
+ Tcl_DeleteHashTable(&fileHash);
+ return ret;
+}
+
+static int
+ZipFSMkZipCmd(ClientData clientData, Tcl_Interp *interp,
+ int argc, CONST char **argv)
+{
+ return ZipFSMkZipOrImgCmd(clientData, interp, 0, argc, argv);
+}
+
+static int
+ZipFSMkImgCmd(ClientData clientData, Tcl_Interp *interp,
+ int argc, CONST char **argv)
+{
+ return ZipFSMkZipOrImgCmd(clientData, interp, 1, argc, argv);
+}
+
+static int
+ZipFSExistsObjCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[])
+{
+ char *filename;
+ int exists;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "filename");
+ return TCL_ERROR;
+ }
+ filename = Tcl_GetStringFromObj(objv[1], 0);
+ ReadLock();
+ exists = ZipFSLookup(filename) != NULL;
+ Unlock();
+ Tcl_SetBooleanObj(Tcl_GetObjResult(interp), exists);
+ return TCL_OK;
+}
+
+static int
+ZipFSInfoObjCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[])
+{
+ char *filename;
+ ZipEntry *z;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "filename");
+ return TCL_ERROR;
+ }
+ filename = Tcl_GetStringFromObj(objv[1], 0);
+ ReadLock();
+ z = ZipFSLookup(filename);
+ if (z != NULL) {
+ Tcl_Obj *result = Tcl_GetObjResult(interp);
+
+ Tcl_ListObjAppendElement(interp, result,
+ Tcl_NewStringObj(z->zipfile->name, -1));
+ Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(z->nbyte));
+ Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(z->nbytecompr));
+ Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(z->offset));
+ }
+ Unlock();
+ return TCL_OK;
+}
+
+static int
+ZipFSListObjCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[])
+{
+ char *pattern = NULL;
+ Tcl_RegExp regexp = NULL;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ Tcl_Obj *result = Tcl_GetObjResult(interp);
+
+ if (objc > 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "?(-glob|-regexp)? ?pattern?");
+ return TCL_ERROR;
+ }
+ if (objc == 3) {
+ int n;
+ char *what = Tcl_GetStringFromObj(objv[1], &n);
+
+ if ((n >= 2) && (strncmp(what, "-glob", n) == 0)) {
+ pattern = Tcl_GetString(objv[2]);
+ } else if ((n >= 2) && (strncmp(what, "-regexp", n) == 0)) {
+ regexp = Tcl_RegExpCompile(interp, Tcl_GetString(objv[2]));
+ if (regexp == NULL) {
+ return TCL_ERROR;
+ }
+ } else {
+ Tcl_AppendResult(interp, "unknown option: ", what, (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if (objc == 2) {
+ pattern = Tcl_GetStringFromObj(objv[1], 0);
+ }
+ ReadLock();
+ if (pattern != NULL) {
+ 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 != NULL) {
+ for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
+ hPtr != NULL; 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 != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
+
+ Tcl_ListObjAppendElement(interp, result,
+ Tcl_NewStringObj(z->name, -1));
+ }
+ }
+ Unlock();
+ return TCL_OK;
+}
+
+static int
+ZipChannelClose(ClientData instanceData, Tcl_Interp *interp)
+{
+ ZipChannel *info = (ZipChannel *) instanceData;
+
+ if (info->iscompr && (info->ubuf != NULL)) {
+ Tcl_Free((char *) info->ubuf);
+ info->ubuf = NULL;
+ }
+ if (info->isenc) {
+ info->isenc = 0;
+ memset(info->keys, 0, sizeof (info->keys));
+ }
+ if (info->iswr) {
+ ZipEntry *z = info->zipentry;
+ unsigned char *newdata;
+
+ newdata =
+ (unsigned char *) Tcl_Realloc((char *) info->ubuf, info->nread);
+ if (newdata != NULL) {
+ if (z->data != NULL) {
+ Tcl_Free((char *) z->data);
+ }
+ z->data = newdata;
+ z->nbyte = z->nbytecompr = info->nbyte;
+ z->cmeth = ZIP_COMPMETH_STORED;
+ z->timestamp = time(NULL);
+ z->isdir = 0;
+ z->isenc = 0;
+ z->offset = 0;
+ z->crc32 = 0;
+ } else {
+ Tcl_Free((char *) info->ubuf);
+ }
+ }
+ WriteLock();
+ info->zipfile->nopen--;
+ Unlock();
+ Tcl_Free((char *) info);
+ return TCL_OK;
+}
+
+static int
+ZipChannelRead(ClientData instanceData, char *buf, int toRead, int *errloc)
+{
+ ZipChannel *info = (ZipChannel *) instanceData;
+ unsigned long nextpos;
+
+ if (info->isdir) {
+ *errloc = EISDIR;
+ return -1;
+ }
+ nextpos = info->nread + toRead;
+ if (nextpos > info->nbyte) {
+ toRead = info->nbyte - info->nread;
+ nextpos = info->nbyte;
+ }
+ if (toRead == 0) {
+ return 0;
+ }
+ if (info->isenc) {
+ int i, ch;
+
+ for (i = 0; i < toRead; i++) {
+ ch = info->ubuf[i + info->nread];
+ buf[i] = zdecode(info->keys, crc32tab, ch);
+ }
+ } else {
+ memcpy(buf, info->ubuf + info->nread, toRead);
+ }
+ info->nread = nextpos;
+ *errloc = 0;
+ return toRead;
+}
+
+static int
+ZipChannelWrite(ClientData instanceData, CONST char *buf,
+ int toWrite, int *errloc)
+{
+ ZipChannel *info = (ZipChannel *) instanceData;
+ unsigned long nextpos;
+
+ if (!info->iswr) {
+ *errloc = EINVAL;
+ return -1;
+ }
+ nextpos = info->nread + toWrite;
+ if (nextpos > info->nmax) {
+ toWrite = info->nmax - info->nread;
+ nextpos = info->nmax;
+ }
+ if (toWrite == 0) {
+ return 0;
+ }
+ memcpy(info->ubuf + info->nread, buf, toWrite);
+ info->nread = nextpos;
+ if (info->nread > info->nbyte) {
+ info->nbyte = info->nread;
+ }
+ *errloc = 0;
+ return toWrite;
+}
+
+static int
+ZipChannelSeek(ClientData instanceData, long offset, int mode, int *errloc)
+{
+ ZipChannel *info = (ZipChannel *) instanceData;
+
+ if (info->isdir) {
+ *errloc = EINVAL;
+ return -1;
+ }
+ switch (mode) {
+ case SEEK_CUR:
+ offset += info->nread;
+ break;
+ case SEEK_END:
+ offset += info->nbyte;
+ break;
+ case SEEK_SET:
+ break;
+ default:
+ *errloc = EINVAL;
+ return -1;
+ }
+ if (info->iswr) {
+ if (offset > info->nmax) {
+ *errloc = EINVAL;
+ return -1;
+ }
+ if (offset > info->nbyte) {
+ info->nbyte = offset;
+ }
+ } else if (offset > info->nbyte) {
+ *errloc = EINVAL;
+ return -1;
+ }
+ if (offset < 0) {
+ *errloc = EINVAL;
+ return -1;
+ }
+ info->nread = (unsigned long) offset;
+ return info->nread;
+}
+
+static void
+ZipChannelWatchChannel(ClientData instanceData, int mask)
+{
+ return;
+}
+static int
+ZipChannelGetFile(ClientData instanceData, int direction,
+ ClientData *handlePtr)
+{
+ return TCL_ERROR;
+}
+
+static Tcl_ChannelType ZipChannelType = {
+ "zip", /* Type name. */
+#ifdef TCL_CHANNEL_VERSION_4
+ TCL_CHANNEL_VERSION_4,
+ ZipChannelClose, /* Close channel, clean instance data */
+ ZipChannelRead, /* Handle read request */
+ ZipChannelWrite, /* Handle write request */
+ ZipChannelSeek, /* Move location of access point, NULL'able */
+ NULL, /* Set options, NULL'able */
+ NULL, /* Get options, NULL'able */
+ ZipChannelWatchChannel, /* Initialize notifier */
+ ZipChannelGetFile, /* Get OS handle from the channel */
+ NULL, /* 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 */
+ NULL, /* Wide seek function, NULL'able */
+ NULL, /* Thread action function, NULL'able */
+#else
+ NULL, /* Set blocking/nonblocking behaviour, NULL'able */
+ ZipChannelClose, /* Close channel, clean instance data */
+ ZipChannelRead, /* Handle read request */
+ ZipChannelWrite, /* Handle write request */
+ ZipChannelSeek, /* Move location of access point, NULL'able */
+ NULL, /* Set options, NULL'able */
+ NULL, /* Get options, NULL'able */
+ ZipChannelWatchChannel, /* Initialize notifier */
+ ZipChannelGetFile, /* Get OS handle from the channel */
+#endif
+};
+
+static Tcl_Channel
+ZipChannelOpen(Tcl_Interp *interp, char *filename, int mode, int permissions)
+{
+ ZipEntry *z;
+ ZipChannel *info;
+ static int count = 1;
+ int i, ch, trunc, wr, flags = 0;
+ char cname[128];
+
+ if ((mode & O_APPEND) ||
+ ((ZipFS.wrmax <= 0) && (mode & (O_WRONLY | O_RDWR)))) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("unsupported open mode", -1));
+ }
+ return NULL;
+ }
+ WriteLock();
+ z = ZipFSLookup(filename);
+ if (z == NULL) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("file not found", -1));
+ }
+ goto error;
+ }
+ trunc = (mode & O_TRUNC) != 0;
+ wr = (mode & (O_WRONLY | O_RDWR)) != 0;
+ if ((z->cmeth != ZIP_COMPMETH_STORED) &&
+ (z->cmeth != ZIP_COMPMETH_DEFLATED)) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("unsupported compression method", -1));
+ }
+ goto error;
+ }
+ if (wr && z->isdir) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("unsupported file type", -1));
+ }
+ goto error;
+ }
+ if (!trunc) {
+ flags |= TCL_READABLE;
+ if (z->isenc && (z->zipfile->pwbuf[0] == 0)) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("decryption failed", -1));
+ }
+ goto error;
+ } else if (wr && (z->data == NULL) && (z->nbyte > ZipFS.wrmax)) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("file too large", -1));
+ }
+ goto error;
+ }
+ } else {
+ flags = TCL_WRITABLE;
+ }
+ info = (ZipChannel *) Tcl_Alloc (sizeof (*info));
+ info->zipfile = z->zipfile;
+ info->zipentry = z;
+ info->nread = 0;
+ if (wr) {
+ flags |= TCL_WRITABLE;
+ info->iswr = 1;
+ info->isdir = 0;
+ info->nmax = ZipFS.wrmax;
+ info->iscompr = 0;
+ info->isenc = 0;
+ info->ubuf = (unsigned char *) Tcl_Alloc(info->nmax);
+ memset(info->ubuf, 0, info->nmax);
+ if (trunc) {
+ info->nbyte = 0;
+ } else {
+ if (z->data != NULL) {
+ i = z->nbyte;
+ if (i > info->nmax) {
+ i = info->nmax;
+ }
+ memcpy(info->ubuf, z->data, i);
+ info->nbyte = i;
+ } else {
+ unsigned char *zbuf = z->zipfile->data + z->offset;
+
+ if (z->isenc) {
+ int len = z->zipfile->pwbuf[0];
+ char pwbuf[260];
+
+ for (i = 0; i < len; i++) {
+ ch = z->zipfile->pwbuf[len - i];
+ pwbuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
+ }
+ pwbuf[i] = '\0';
+ init_keys(pwbuf, info->keys, crc32tab);
+ memset(pwbuf, 0, sizeof (pwbuf));
+ for (i = 0; i < 12; i++) {
+ ch = info->ubuf[i];
+ zdecode(info->keys, crc32tab, ch);
+ }
+ zbuf += i;
+ }
+ if (z->cmeth == ZIP_COMPMETH_DEFLATED) {
+ z_stream stream;
+ int err;
+ unsigned char *cbuf = NULL;
+
+ memset(&stream, 0, sizeof (stream));
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+ stream.avail_in = z->nbytecompr;
+ if (z->isenc) {
+ stream.avail_in -= 12;
+ cbuf = (unsigned char *) Tcl_Alloc(stream.avail_in);
+ for (i = 0; i < stream.avail_in; i++) {
+ ch = info->ubuf[i];
+ cbuf[i] = zdecode(info->keys, crc32tab, ch);
+ }
+ stream.next_in = cbuf;
+ } else {
+ stream.next_in = zbuf;
+ }
+ stream.next_out = info->ubuf;
+ stream.avail_out = info->nmax;
+ if (inflateInit2(&stream, -15) != Z_OK) {
+ goto cerror0;
+ }
+ err = inflate(&stream, Z_SYNC_FLUSH);
+ inflateEnd(&stream);
+ if ((err == Z_STREAM_END) ||
+ ((err == Z_OK) && (stream.avail_in == 0))) {
+ if (cbuf != NULL) {
+ memset(info->keys, 0, sizeof (info->keys));
+ Tcl_Free((char *) cbuf);
+ }
+ goto wrapchan;
+ }
+cerror0:
+ if (cbuf != NULL) {
+ memset(info->keys, 0, sizeof (info->keys));
+ Tcl_Free((char *) cbuf);
+ }
+ if (info->ubuf != NULL) {
+ Tcl_Free((char *) info->ubuf);
+ }
+ Tcl_Free((char *) info);
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("decompression error", -1));
+ }
+ goto error;
+ } else if (z->isenc) {
+ for (i = 0; i < z->nbyte - 12; i++) {
+ ch = zbuf[i];
+ info->ubuf[i] = zdecode(info->keys, crc32tab, ch);
+ }
+ } else {
+ memcpy(info->ubuf, zbuf, z->nbyte);
+ }
+ memset(info->keys, 0, sizeof (info->keys));
+ goto wrapchan;
+ }
+ }
+ } else if (z->data != NULL) {
+ flags |= TCL_READABLE;
+ info->iswr = 0;
+ info->iscompr = 0;
+ info->isdir = 0;
+ info->isenc = 0;
+ info->nbyte = z->nbyte;
+ info->nmax = 0;
+ info->ubuf = z->data;
+ } else {
+ flags |= TCL_READABLE;
+ info->iswr = 0;
+ info->iscompr = z->cmeth == ZIP_COMPMETH_DEFLATED;
+ info->ubuf = z->zipfile->data + z->offset;
+ info->isdir = z->isdir;
+ info->isenc = z->isenc;
+ info->nbyte = z->nbyte;
+ info->nmax = 0;
+ if (info->isenc) {
+ int len = z->zipfile->pwbuf[0];
+ char pwbuf[260];
+
+ for (i = 0; i < len; i++) {
+ ch = z->zipfile->pwbuf[len - i];
+ pwbuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
+ }
+ pwbuf[i] = '\0';
+ init_keys(pwbuf, info->keys, crc32tab);
+ memset(pwbuf, 0, sizeof (pwbuf));
+ for (i = 0; i < 12; i++) {
+ ch = info->ubuf[i];
+ zdecode(info->keys, crc32tab, ch);
+ }
+ info->ubuf += i;
+ }
+ if (info->iscompr) {
+ z_stream stream;
+ int err;
+ unsigned char *ubuf = NULL;
+
+ memset(&stream, 0, sizeof (stream));
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+ stream.avail_in = z->nbytecompr;
+ if (info->isenc) {
+ stream.avail_in -= 12;
+ ubuf = (unsigned char *) Tcl_Alloc(stream.avail_in);
+ for (i = 0; i < stream.avail_in; i++) {
+ ch = info->ubuf[i];
+ ubuf[i] = zdecode(info->keys, crc32tab, ch);
+ }
+ stream.next_in = ubuf;
+ } else {
+ stream.next_in = info->ubuf;
+ }
+ stream.next_out = info->ubuf =
+ (unsigned char *) Tcl_Alloc(info->nbyte);
+ stream.avail_out = info->nbyte;
+ if (inflateInit2(&stream, -15) != Z_OK) {
+ goto cerror;
+ }
+ err = inflate(&stream, Z_SYNC_FLUSH);
+ inflateEnd(&stream);
+ if ((err == Z_STREAM_END) ||
+ ((err == Z_OK) && (stream.avail_in == 0))) {
+ if (ubuf != NULL) {
+ info->isenc = 0;
+ memset(info->keys, 0, sizeof (info->keys));
+ Tcl_Free((char *) ubuf);
+ }
+ goto wrapchan;
+ }
+cerror:
+ if (ubuf != NULL) {
+ info->isenc = 0;
+ memset(info->keys, 0, sizeof (info->keys));
+ Tcl_Free((char *) ubuf);
+ }
+ if (info->ubuf != NULL) {
+ Tcl_Free((char *) info->ubuf);
+ }
+ Tcl_Free((char *) info);
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("decompression error", -1));
+ }
+ goto error;
+ }
+ }
+wrapchan:
+ sprintf(cname, "zipfs_%lx_%d", (unsigned long) z->offset, count++);
+ z->zipfile->nopen++;
+ Unlock();
+ return Tcl_CreateChannel(&ZipChannelType, cname, (ClientData) info, flags);
+
+error:
+ Unlock();
+ return NULL;
+}
+
+static int
+ZipEntryStat(char *path, Tcl_StatBuf *buf)
+{
+ ZipEntry *z;
+ int ret = -1;
+
+ ReadLock();
+ z = ZipFSLookup(path);
+ if (z == NULL) {
+ goto done;
+ }
+ memset(buf, 0, sizeof (Tcl_StatBuf));
+ if (z->isdir) {
+ buf->st_mode = S_IFDIR | 0555;
+ } else {
+ buf->st_mode = S_IFREG | 0555;
+ }
+ buf->st_size = z->nbyte;
+ buf->st_mtime = z->timestamp;
+ buf->st_ctime = z->timestamp;
+ buf->st_atime = z->timestamp;
+ ret = 0;
+done:
+ Unlock();
+ return ret;
+}
+
+static int
+ZipEntryAccess(char *path, int mode)
+{
+ ZipEntry *z;
+
+ if (mode & 3) {
+ return -1;
+ }
+ ReadLock();
+ z = ZipFSLookup(path);
+ Unlock();
+ return (z != NULL) ? 0 : -1;
+}
+
+static Tcl_Channel
+Zip_FSOpenFileChannelProc(Tcl_Interp *interp, Tcl_Obj *pathPtr,
+ int mode, int permissions)
+{
+ int len;
+
+ return ZipChannelOpen(interp, Tcl_GetStringFromObj(pathPtr, &len),
+ mode, permissions);
+}
+
+static int
+Zip_FSStatProc(Tcl_Obj *pathPtr, Tcl_StatBuf *buf)
+{
+ int len;
+
+ return ZipEntryStat(Tcl_GetStringFromObj(pathPtr, &len), buf);
+}
+
+static int
+Zip_FSAccessProc(Tcl_Obj *pathPtr, int mode)
+{
+ int len;
+
+ return ZipEntryAccess(Tcl_GetStringFromObj(pathPtr, &len), mode);
+}
+
+static Tcl_Obj *
+Zip_FSFilesystemSeparatorProc(Tcl_Obj *pathPtr)
+{
+ return Tcl_NewStringObj("/", -1);
+}
+
+static int
+Zip_FSMatchInDirectoryProc(Tcl_Interp* interp, Tcl_Obj *result,
+ Tcl_Obj *pathPtr, CONST char *pattern,
+ Tcl_GlobTypeData *types)
+{
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ int scnt, len, l, dirOnly = -1, prefixLen, strip = 0;
+ char *pat, *prefix, *path;
+#if defined(_WIN32) || defined(_WIN64)
+ char drivePrefix[3];
+#endif
+ Tcl_DString ds, dsPref;
+
+#if defined(_WIN32) || defined(_WIN64)
+ if ((pattern != NULL) && (pattern[0] != '\0') &&
+ (strchr(alpha, pattern[0]) != NULL) && (pattern[1] == ':')) {
+ pattern += 2;
+ }
+#endif
+ if (types != NULL) {
+ dirOnly = (types->type & TCL_GLOB_TYPE_DIR) == TCL_GLOB_TYPE_DIR;
+ }
+ Tcl_DStringInit(&ds);
+ Tcl_DStringInit(&dsPref);
+ prefix = Tcl_GetStringFromObj(pathPtr, &prefixLen);
+ Tcl_DStringAppend(&dsPref, prefix, prefixLen);
+ prefix = Tcl_DStringValue(&dsPref);
+ path = AbsolutePath(prefix, &ds);
+ len = Tcl_DStringLength(&ds);
+ if (strcmp(prefix, path) == 0) {
+ prefix = NULL;
+ } else {
+#if defined(_WIN32) || defined(_WIN64)
+ if ((strchr(alpha, prefix[0]) != NULL) && (prefix[1] == ':')) {
+ if (strcmp(prefix + 2, path) == 0) {
+ strncpy(drivePrefix, prefix, 3);
+ drivePrefix[2] = '\0';
+ prefix = drivePrefix;
+ }
+ } else {
+ strip = len + 1;
+ }
+#else
+ strip = len + 1;
+#endif
+ }
+ if (prefix != NULL) {
+#if defined(_WIN32) || defined(_WIN64)
+ if (prefix == drivePrefix) {
+ Tcl_DStringSetLength(&dsPref, 0);
+ Tcl_DStringAppend(&dsPref, drivePrefix, -1);
+ prefixLen = Tcl_DStringLength(&dsPref);
+ } else {
+ Tcl_DStringAppend(&dsPref, "/", 1);
+ prefixLen++;
+ }
+ prefix = Tcl_DStringValue(&dsPref);
+#else
+ Tcl_DStringAppend(&dsPref, "/", 1);
+ prefixLen++;
+ prefix = Tcl_DStringValue(&dsPref);
+#endif
+ }
+ ReadLock();
+ if ((types != NULL) && (types->type == TCL_GLOB_TYPE_MOUNT)) {
+ l = CountSlashes(path);
+ if (path[len - 1] == '/') {
+ len--;
+ } else {
+ l++;
+ }
+ if ((pattern == NULL) || (pattern[0] == '\0')) {
+ pattern = "*";
+ }
+ hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search);
+ while (hPtr != NULL) {
+ ZipFile *zf = (ZipFile *) Tcl_GetHashValue(hPtr);
+
+ if (zf->mntptlen == 0) {
+ ZipEntry *z = zf->topents;
+
+ while (z != NULL) {
+ int 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)) {
+ if (prefix != NULL) {
+ Tcl_DStringAppend(&dsPref, z->name, lenz);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
+ Tcl_DStringLength(&dsPref)));
+ Tcl_DStringSetLength(&dsPref, prefixLen);
+ } else {
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(z->name, lenz));
+ }
+ }
+ z = z->tnext;
+ }
+ } else if ((zf->mntptlen > len + 1) &&
+ (strncmp(zf->mntpt, path, len) == 0) &&
+ (zf->mntpt[len] == '/') &&
+ (CountSlashes(zf->mntpt) == l) &&
+ Tcl_StringCaseMatch(zf->mntpt + len + 1, pattern, 0)) {
+ if (prefix != NULL) {
+ Tcl_DStringAppend(&dsPref, zf->mntpt, zf->mntptlen);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
+ Tcl_DStringLength(&dsPref)));
+ Tcl_DStringSetLength(&dsPref, prefixLen);
+ } else {
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(zf->mntpt, zf->mntptlen));
+ }
+ }
+ hPtr = Tcl_NextHashEntry(&search);
+ }
+ goto end;
+ }
+ if ((pattern == NULL) || (pattern[0] == '\0')) {
+ hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, path);
+ if (hPtr != NULL) {
+ ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
+
+ if ((dirOnly < 0) ||
+ (!dirOnly && !z->isdir) ||
+ (dirOnly && z->isdir)) {
+ if (prefix != NULL) {
+ Tcl_DStringAppend(&dsPref, z->name, -1);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
+ Tcl_DStringLength(&dsPref)));
+ Tcl_DStringSetLength(&dsPref, prefixLen);
+ } else {
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(z->name, -1));
+ }
+ }
+ }
+ goto end;
+ }
+ l = strlen(pattern);
+ pat = Tcl_Alloc(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);
+ for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
+
+ if ((dirOnly >= 0) &&
+ ((dirOnly && !z->isdir) || (!dirOnly && z->isdir))) {
+ continue;
+ }
+ if ((z->depth == scnt) && Tcl_StringCaseMatch(z->name, pat, 0)) {
+ if (prefix != NULL) {
+ Tcl_DStringAppend(&dsPref, z->name + strip, -1);
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
+ Tcl_DStringLength(&dsPref)));
+ Tcl_DStringSetLength(&dsPref, prefixLen);
+ } else {
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(z->name + strip, -1));
+ }
+ }
+ }
+ Tcl_Free(pat);
+end:
+ Unlock();
+ Tcl_DStringFree(&dsPref);
+ Tcl_DStringFree(&ds);
+ return TCL_OK;
+}
+
+static int
+Zip_FSNormalizePathProc(Tcl_Interp *interp, Tcl_Obj *pathPtr,
+ int nextCheckpoint)
+{
+ char *path;
+ Tcl_DString ds;
+ int len;
+
+ path = Tcl_GetStringFromObj(pathPtr, &len);
+ Tcl_DStringInit(&ds);
+ path = AbsolutePath(path, &ds);
+ nextCheckpoint = Tcl_DStringLength(&ds);
+ Tcl_SetStringObj(pathPtr, Tcl_DStringValue(&ds),
+ Tcl_DStringLength(&ds));
+ Tcl_DStringFree(&ds);
+ return nextCheckpoint;
+}
+
+static int
+Zip_FSPathInFilesystemProc(Tcl_Obj *pathPtr, ClientData *clientDataPtr)
+{
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ ZipFile *zf;
+ int ret = -1, len;
+ char *path;
+ Tcl_DString ds;
+
+ path = Tcl_GetStringFromObj(pathPtr, &len);
+ Tcl_DStringInit(&ds);
+ path = AbsolutePath(path, &ds);
+ len = Tcl_DStringLength(&ds);
+#if defined(_WIN32) || defined(_WIN64)
+ if (len && (strchr(alpha, path[0]) != NULL) && (path[1] == ':')) {
+ path += 2;
+ len -= 2;
+ }
+#endif
+ ReadLock();
+ hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, path);
+ if (hPtr != NULL) {
+ ret = TCL_OK;
+ goto endloop;
+ }
+ hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search);
+ while (hPtr != NULL) {
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
+ if (zf->mntptlen == 0) {
+ ZipEntry *z = zf->topents;
+
+ while (z != NULL) {
+ int lenz = strlen(z->name);
+
+ if ((len >= lenz) &&
+ (strncmp(path, z->name, lenz) == 0)) {
+ ret = TCL_OK;
+ goto endloop;
+ }
+ z = z->tnext;
+ }
+ } else if ((len >= zf->mntptlen) &&
+ (strncmp(path, zf->mntpt, zf->mntptlen) == 0)) {
+ ret = TCL_OK;
+ goto endloop;
+ }
+ hPtr = Tcl_NextHashEntry(&search);
+ }
+endloop:
+ Unlock();
+ Tcl_DStringFree(&ds);
+ return ret;
+}
+
+static Tcl_Obj *
+Zip_FSListVolumesProc(void)
+{
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ ZipFile *zf;
+ Tcl_Obj *vols = Tcl_NewObj(), *vol;
+
+ ReadLock();
+ hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search);
+ while (hPtr != NULL) {
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
+ vol = Tcl_NewStringObj(zf->mntpt, zf->mntptlen);
+ Tcl_ListObjAppendList(NULL, vols, vol);
+ Tcl_DecrRefCount(vol);
+ hPtr = Tcl_NextHashEntry(&search);
+ }
+ Unlock();
+ return vols;
+}
+
+static int
+Zip_FSChdirProc(Tcl_Obj *pathPtr)
+{
+ int len;
+ char *path;
+ Tcl_DString ds;
+ ZipEntry *z;
+ int ret = TCL_OK;
+
+ path = Tcl_GetStringFromObj(pathPtr, &len);
+ Tcl_DStringInit(&ds);
+ path = AbsolutePath(path, &ds);
+ ReadLock();
+ z = ZipFSLookup(path);
+ if ((z == NULL) || !z->isdir) {
+ Tcl_SetErrno(ENOENT);
+ ret = -1;
+ }
+ Unlock();
+ Tcl_DStringFree(&ds);
+ return ret;
+}
+
+static CONST char *CONST86 *
+Zip_FSFileAttrStringsProc(Tcl_Obj *pathPtr, Tcl_Obj** objPtrRef)
+{
+ static CONST char *attrs[] = {
+ "-uncompsize",
+ "-compsize",
+ "-offset",
+ "-mount",
+ "-archive",
+ "-permissions",
+ NULL,
+ };
+
+ return attrs;
+}
+
+static int
+Zip_FSFileAttrsGetProc(Tcl_Interp *interp, int index, Tcl_Obj *pathPtr,
+ Tcl_Obj **objPtrRef)
+{
+ int len, ret = TCL_OK;
+ char *path;
+ ZipEntry *z;
+
+ path = Tcl_GetStringFromObj(pathPtr, &len);
+ ReadLock();
+ z = ZipFSLookup(path);
+ if (z == NULL) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("file not found", -1));
+ }
+ ret = TCL_ERROR;
+ goto done;
+ }
+ switch (index) {
+ case 0:
+ *objPtrRef = Tcl_NewIntObj(z->nbyte);
+ goto done;
+ case 1:
+ *objPtrRef= Tcl_NewIntObj(z->nbytecompr);
+ goto done;
+ case 2:
+ *objPtrRef= Tcl_NewLongObj(z->offset);
+ goto done;
+ case 3:
+ *objPtrRef= Tcl_NewStringObj(z->zipfile->mntpt, -1);
+ goto done;
+ case 4:
+ *objPtrRef= Tcl_NewStringObj(z->zipfile->name, -1);
+ goto done;
+ case 5:
+ *objPtrRef= Tcl_NewStringObj("0555", -1);
+ goto done;
+ }
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("unknown attribute", -1));
+ }
+ ret = TCL_ERROR;
+done:
+ Unlock();
+ return ret;
+}
+
+static int
+Zip_FSFileAttrsSetProc(Tcl_Interp *interp, int index, Tcl_Obj *pathPtr,
+ Tcl_Obj *objPtr)
+{
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("unsupported operation", -1));
+ }
+ return TCL_ERROR;
+}
+
+
+static Tcl_Obj *
+Zip_FSFilesystemPathTypeProc(Tcl_Obj *pathPtr)
+{
+ return Tcl_NewStringObj("zip", -1);
+}
+
+#ifndef ANDROID
+static int
+Zip_FSLoadFile(Tcl_Interp *interp, Tcl_Obj *path, Tcl_LoadHandle *loadHandle,
+ Tcl_FSUnloadFileProc **unloadProcPtr, int flags)
+{
+ Tcl_FSLoadFileProc2 *loadFileProc;
+ Tcl_Obj *altPath = NULL;
+ int ret = -1;
+
+ if (Tcl_FSAccess(path, R_OK) == 0) {
+ /*
+ * EXDEV should trigger loading by copying to temp store.
+ */
+ Tcl_SetErrno(EXDEV);
+ return ret;
+ } else {
+ Tcl_Obj *objs[2] = { NULL, NULL };
+
+ objs[1] = TclPathPart(interp, path, TCL_PATH_DIRNAME);
+ if ((objs[1] != NULL) && (Zip_FSAccessProc(objs[1], R_OK) == 0)) {
+ /*
+ * 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);
+ objs[0] = TclPathPart(interp, TclGetObjNameOfExecutable(),
+ TCL_PATH_DIRNAME);
+ if (objs[0] != NULL) {
+ altPath = TclJoinPath(2, objs);
+ if (altPath != NULL) {
+ Tcl_IncrRefCount(altPath);
+ if (Tcl_FSAccess(altPath, R_OK) == 0) {
+ path = altPath;
+ }
+ }
+ }
+ }
+ if (objs[0] != NULL) {
+ Tcl_DecrRefCount(objs[0]);
+ }
+ if (objs[1] != NULL) {
+ Tcl_DecrRefCount(objs[1]);
+ }
+ }
+ loadFileProc = (Tcl_FSLoadFileProc2 *) tclNativeFilesystem.loadFileProc;
+ if (loadFileProc != NULL) {
+ ret = loadFileProc(interp, path, loadHandle, unloadProcPtr, flags);
+ } else {
+ Tcl_SetErrno(ENOENT);
+ }
+ if (altPath != NULL) {
+ Tcl_DecrRefCount(altPath);
+ }
+ return ret;
+}
+#endif
+
+Tcl_Filesystem zipfsFilesystem = {
+ "zipfs",
+ sizeof (Tcl_Filesystem),
+ TCL_FILESYSTEM_VERSION_2,
+ Zip_FSPathInFilesystemProc,
+ NULL, /* dupInternalRepProc */
+ NULL, /* freeInternalRepProc */
+ NULL, /* internalToNormalizedProc */
+ NULL, /* createInternalRepProc */
+ Zip_FSNormalizePathProc,
+ Zip_FSFilesystemPathTypeProc,
+ Zip_FSFilesystemSeparatorProc,
+ Zip_FSStatProc,
+ Zip_FSAccessProc,
+ Zip_FSOpenFileChannelProc,
+ Zip_FSMatchInDirectoryProc,
+ NULL, /* utimeProc */
+ NULL, /* linkProc */
+ Zip_FSListVolumesProc,
+ Zip_FSFileAttrStringsProc,
+ Zip_FSFileAttrsGetProc,
+ Zip_FSFileAttrsSetProc,
+ NULL, /* createDirectoryProc */
+ NULL, /* removeDirectoryProc */
+ NULL, /* deleteFileProc */
+ NULL, /* copyFileProc */
+ NULL, /* renameFileProc */
+ NULL, /* copyDirectoryProc */
+ NULL, /* lstatProc */
+#ifdef ANDROID
+ NULL, /* loadFileProc */
+#else
+ (Tcl_FSLoadFileProc *) Zip_FSLoadFile,
+#endif
+ NULL, /* getCwdProc */
+ Zip_FSChdirProc,
+};
+
+#endif /* HAVE_ZLIB */
+
+static int
+Zipfs_doInit(Tcl_Interp *interp, int safe)
+{
+#ifdef HAVE_ZLIB
+ static CONST char findproc[] =
+ "proc ::zipfs::find d {\n"
+ " set ret {}\n"
+ " foreach f [glob -directory $d -tails -nocomplain * .*] {\n"
+ " if {$f eq \".\" || $f eq \"..\"} {\n"
+ " continue\n"
+ " }\n"
+ " set f [file join $d $f]\n"
+ " lappend ret $f\n"
+ " foreach f [::zipfs::find $f] {\n"
+ " lappend ret $f\n"
+ " }\n"
+ " }\n"
+ " return [lsort $ret]\n"
+ "}\n";
+
+#ifdef USE_TCL_STUBS
+ if (Tcl_InitStubs(interp, "8.0", 0) == NULL) {
+ return TCL_ERROR;
+ }
+#endif
+ /* one-time initialization */
+ WriteLock();
+ if (!ZipFS.initialized) {
+ static const Tcl_Time t = { 0, 0 };
+
+ /* inflate condition */
+ Tcl_MutexLock(&ZipFSMutex);
+ Tcl_ConditionWait(&ZipFSCond, &ZipFSMutex, &t);
+ Tcl_MutexUnlock(&ZipFSMutex);
+#ifdef ANDROID
+ /* force loadFileProc to native one */
+ zipfsFilesystem.loadFileProc = tclNativeFilesystem.loadFileProc;
+#endif
+ Tcl_FSRegister(NULL, &zipfsFilesystem);
+ Tcl_InitHashTable(&ZipFS.fileHash, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&ZipFS.zipHash, TCL_STRING_KEYS);
+ ZipFS.initialized = 1;
+#if defined(ZIPFS_IN_TCL) || defined(ZIPFS_IN_TK)
+ Tcl_StaticPackage(interp, "zipfs", Zipfs_Init, Zipfs_SafeInit);
+#endif
+ }
+ Unlock();
+#if !defined(ZIPFS_IN_TCL) && !defined(ZIPFS_IN_TK)
+ Tcl_PkgProvide(interp, "zipfs", "1.0");
+#endif
+ if (!safe) {
+ Tcl_CreateCommand(interp, "::zipfs::mount", ZipFSMountCmd, 0, 0);
+ Tcl_CreateCommand(interp, "::zipfs::unmount", ZipFSUnmountCmd, 0, 0);
+ Tcl_CreateCommand(interp, "::zipfs::mkkey", ZipFSMkKeyCmd, 0, 0);
+ Tcl_CreateCommand(interp, "::zipfs::mkimg", ZipFSMkImgCmd, 0, 0);
+ Tcl_CreateCommand(interp, "::zipfs::mkzip", ZipFSMkZipCmd, 0, 0);
+ Tcl_GlobalEval(interp, findproc);
+ }
+ Tcl_CreateObjCommand(interp, "::zipfs::exists", ZipFSExistsObjCmd, 0, 0);
+ Tcl_CreateObjCommand(interp, "::zipfs::info", ZipFSInfoObjCmd, 0, 0);
+ Tcl_CreateObjCommand(interp, "::zipfs::list", ZipFSListObjCmd, 0, 0);
+ if (!safe) {
+ Tcl_LinkVar(interp, "::zipfs::wrmax", (char *) &ZipFS.wrmax,
+ TCL_LINK_INT);
+ }
+ return TCL_OK;
+#else
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("no zlib available", -1));
+ }
+ return TCL_ERROR;
+#endif
+}
+
+int
+Zipfs_Init(Tcl_Interp *interp)
+{
+ return Zipfs_doInit(interp, 0);
+}
+
+int
+Zipfs_SafeInit(Tcl_Interp *interp)
+{
+ return Zipfs_doInit(interp, 1);
+}
+
+#ifndef HAVE_ZLIB
+
+int
+Zipfs_Mount(Tcl_Interp *interp, CONST char *zipname, CONST char *mntpt,
+ CONST char *passwd)
+{
+ return Zipfs_doInit(interp, 1);
+}
+
+int
+Zipfs_Unmount(Tcl_Interp *interp, CONST char *zipname)
+{
+ return Zipfs_doInit(interp, 1);
+}
+
+#endif
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */
diff --git a/generic/zipfs.h b/generic/zipfs.h
new file mode 100644
index 0000000..587fed8
--- /dev/null
+++ b/generic/zipfs.h
@@ -0,0 +1,43 @@
+#ifndef _ZIPFS_H
+#define _ZIPFS_H
+
+#ifdef ZIPFS_IN_TK
+#include "tkInt.h"
+#define Zipfs_Mount Tkzipfs_Mount
+#define Zipfs_Unmount Tkzipfs_Unmount
+#define Zipfs_Init Tkzipfs_Init
+#define Zipfs_SafeInit Tkzipfs_SafeInit
+#endif
+
+#ifdef ZIPFS_IN_TCL
+#include "tclPort.h"
+#define Zipfs_Mount Tclzipfs_Mount
+#define Zipfs_Unmount Tclzipfs_Unmount
+#define Zipfs_Init Tclzipfs_Init
+#define Zipfs_SafeInit Tclzipfs_SafeInit
+#endif
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+#ifdef BUILD_tcl
+#undef EXTERN
+#define EXTERN
+#endif
+
+EXTERN int Zipfs_Mount(Tcl_Interp *interp, CONST char *zipname,
+ CONST char *mntpt, CONST char *passwd);
+EXTERN int Zipfs_Unmount(Tcl_Interp *interp, CONST char *mountname);
+EXTERN int Zipfs_Init(Tcl_Interp *interp);
+EXTERN int Zipfs_SafeInit(Tcl_Interp *interp);
+
+#endif
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */
diff --git a/pkgs/Android.mk b/pkgs/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/pkgs/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/tcl-config.mk b/tcl-config.mk
new file mode 100644
index 0000000..ae14b6b
--- /dev/null
+++ b/tcl-config.mk
@@ -0,0 +1,60 @@
+tcl_includes := $(tcl_path)/generic $(tcl_path)/unix
+
+tcl_cflags := \
+ -DHAVE_SYS_SELECT_H=1 \
+ -DHAVE_LIMITS_H=1 \
+ -DHAVE_UNISTD_H=1 \
+ -DHAVE_SYS_PARAM_H=1 \
+ -D_LARGEFILE64_SOURCE=1 \
+ -DTCL_WIDE_INT_TYPE="long long" \
+ -DTCL_SHLIB_EXT="\".so\"" \
+ -DHAVE_CAST_TO_UNION=1 \
+ -DHAVE_GETCWD=1 \
+ -DHAVE_OPENDIR=1 \
+ -DHAVE_MKSTEMP=1 \
+ -DHAVE_MKSTEMPS=1 \
+ -DHAVE_STRSTR=1 \
+ -DHAVE_STRTOL=1 \
+ -DHAVE_STRTOLL=1 \
+ -DHAVE_STRTOULL=1 \
+ -DHAVE_TMPNAM=1 \
+ -DHAVE_WAITPID=1 \
+ -DHAVE_STRUCT_ADDRINFO=1 \
+ -DHAVE_STRUCT_IN6_ADDR=1 \
+ -DHAVE_STRUCT_SOCKADDR_IN6=1 \
+ -DHAVE_STRUCT_SOCKADDR_STORAGE=1 \
+ -DUSE_TERMIOS=1 \
+ -DHAVE_MKTIME=1 \
+ -DUSE_INTERP_ERRORLINE=1 \
+ -DHAVE_SYS_TIME_H=1 \
+ -DTIME_WITH_SYS_TIME=1 \
+ -DHAVE_TM_ZONE=1 \
+ -DHAVE_GMTIME_R=1 \
+ -DHAVE_LOCALTIME_R=1 \
+ -DHAVE_TM_GMTOFF=1 \
+ -DHAVE_TIMEZONE_VAR=1 \
+ -DHAVE_ST_BLKSIZE=1 \
+ -DSTDC_HEADERS=1 \
+ -DHAVE_INTPTR_T=1 \
+ -DHAVE_UINTPTR_T=1 \
+ -DHAVE_SIGNED_CHAR=1 \
+ -DHAVE_SYS_IOCTL_H=1 \
+ -DHAVE_MEMCPY=1 \
+ -DHAVE_MEMMOVE=1 \
+ -DVOID=void \
+ -DNO_UNION_WAIT=1 \
+ -DHAVE_ZLIB=1 \
+ -DMP_PREC=4 \
+ -DTCL_TOMMATH=1 \
+ -D_REENTRANT=1 \
+ -D_THREADSAFE=1 \
+ -DTCL_THREADS=1 \
+ -DTCL_PTHREAD_ATFORK=1 \
+ -DUSE_THREAD_ALLOC=1 \
+ -DTCL_CFGVAL_ENCODING="\"utf-8\"" \
+ -DTCL_UNLOAD_DLLS=1 \
+ -DTCL_CFG_OPTIMIZED=1 \
+ -DZIPFS_IN_TCL=1 \
+ -DTCL_PACKAGE_PATH="\"/assets\"" \
+ -DTCL_LIBRARY="\"/assets/tcl8.6\""
+
diff --git a/unix/Makefile.in b/unix/Makefile.in
index 18c90fa..eb1ba3c 100644
--- a/unix/Makefile.in
+++ b/unix/Makefile.in
@@ -308,7 +308,7 @@ GENERIC_OBJS = regcomp.o regexec.o regfree.o regerror.o tclAlloc.o \
tclStrToD.o tclThread.o \
tclThreadAlloc.o tclThreadJoin.o tclThreadStorage.o tclStubInit.o \
tclTimer.o tclTrace.o tclUtf.o tclUtil.o tclVar.o tclZlib.o \
- tclTomMathInterface.o
+ tclTomMathInterface.o zipfs.o
OO_OBJS = tclOO.o tclOOBasic.o tclOOCall.o tclOODefineCmds.o tclOOInfo.o \
tclOOMethod.o tclOOStubInit.o
@@ -382,7 +382,8 @@ GENERIC_HDRS = \
$(GENERIC_DIR)/tclPatch.h \
$(GENERIC_DIR)/tclPlatDecls.h \
$(GENERIC_DIR)/tclPort.h \
- $(GENERIC_DIR)/tclRegexp.h
+ $(GENERIC_DIR)/tclRegexp.h \
+ $(GENERIC_DIR)/zipfs.h
GENERIC_SRCS = \
$(GENERIC_DIR)/regcomp.c \
@@ -463,7 +464,8 @@ GENERIC_SRCS = \
$(GENERIC_DIR)/tclUtil.c \
$(GENERIC_DIR)/tclVar.c \
$(GENERIC_DIR)/tclAssembly.c \
- $(GENERIC_DIR)/tclZlib.c
+ $(GENERIC_DIR)/tclZlib.c \
+ $(GENERIC_DIR)/zipfs.c
OO_SRCS = \
$(GENERIC_DIR)/tclOO.c \
@@ -1321,6 +1323,9 @@ tclVar.o: $(GENERIC_DIR)/tclVar.c
tclZlib.o: $(GENERIC_DIR)/tclZlib.c
$(CC) -c $(CC_SWITCHES) $(ZLIB_INCLUDE) $(GENERIC_DIR)/tclZlib.c
+zipfs.o: $(GENERIC_DIR)/zipfs.c
+ $(CC) -c $(CC_SWITCHES) $(ZLIB_INCLUDE) $(GENERIC_DIR)/zipfs.c
+
tclTest.o: $(GENERIC_DIR)/tclTest.c $(IOHDR) $(TCLREHDRS)
$(CC) -c $(APP_CC_SWITCHES) $(GENERIC_DIR)/tclTest.c
diff --git a/unix/tclLoadDl.c b/unix/tclLoadDl.c
index dc711f8..3376d94 100644
--- a/unix/tclLoadDl.c
+++ b/unix/tclLoadDl.c
@@ -97,7 +97,11 @@ TclpDlopen(
} else {
dlopenflags |= RTLD_NOW;
}
- handle = dlopen(native, dlopenflags);
+ if (native == NULL) {
+ handle = NULL;
+ } else {
+ handle = dlopen(native, dlopenflags);
+ }
if (handle == NULL) {
/*
* Let the OS loader examine the binary search path for whatever
@@ -115,7 +119,41 @@ TclpDlopen(
handle = dlopen(native, dlopenflags);
Tcl_DStringFree(&ds);
}
+#ifdef ANDROID
+ /*
+ * If not an absolute or relative path, try to load
+ * from $INTERNAL_STORAGE/../lib (the place where the
+ * system has installed bundled .so files from the .APK)
+ */
+ if (handle == NULL) {
+ native = Tcl_GetString(pathPtr);
+ if ((native != NULL) && (strchr(native, '/') == NULL)) {
+ char *storage = getenv("INTERNAL_STORAGE");
+ Tcl_DString ds2;
+ if ((storage != NULL) && (storage[0] != '\0')) {
+ Tcl_DStringInit(&ds2);
+ Tcl_DStringAppend(&ds2, storage, -1);
+ Tcl_DStringAppend(&ds2, "/../lib/", -1);
+ Tcl_DStringAppend(&ds2, native, -1);
+ handle = dlopen(Tcl_DStringValue(&ds2), RTLD_NOW | RTLD_GLOBAL);
+ Tcl_DStringFree(&ds2);
+ }
+ if (handle == NULL) {
+ storage = getenv("TK_TCL_WISH_LD_LIBS");
+ if ((storage != NULL) && (storage[0] != '\0')) {
+ Tcl_DStringInit(&ds2);
+ Tcl_DStringAppend(&ds2, storage, -1);
+ Tcl_DStringAppend(&ds2, "/", -1);
+ Tcl_DStringAppend(&ds2, native, -1);
+ handle =
+ dlopen(Tcl_DStringValue(&ds2), RTLD_NOW | RTLD_GLOBAL);
+ Tcl_DStringFree(&ds2);
+ }
+ }
+ }
+ }
+ #endif
if (handle == NULL) {
/*
* Write the string to a variable first to work around a compiler bug
diff --git a/unix/tclUnixFCmd.c b/unix/tclUnixFCmd.c
index 3b1b6ca..0193dae 100644
--- a/unix/tclUnixFCmd.c
+++ b/unix/tclUnixFCmd.c
@@ -465,6 +465,9 @@ DoCopyFile(
/* Used to determine filetype. */
{
Tcl_StatBuf dstStatBuf;
+#ifdef ANDROID
+ int ret;
+#endif
if (S_ISDIR(statBufPtr->st_mode)) {
errno = EISDIR;
@@ -520,7 +523,15 @@ DoCopyFile(
if (mkfifo(dst, statBufPtr->st_mode) < 0) { /* INTL: Native. */
return TCL_ERROR;
}
+#ifdef ANDROID
+ ret = CopyFileAtts(src, dst, statBufPtr);
+ if (ret != TCL_OK && errno == EPERM) {
+ ret = TCL_OK;
+ }
+ return ret;
+#else
return CopyFileAtts(src, dst, statBufPtr);
+#endif
default:
return TclUnixCopyFile(src, dst, statBufPtr, 0);
}
@@ -629,6 +640,11 @@ TclUnixCopyFile(
return TCL_ERROR;
}
if (!dontCopyAtts && CopyFileAtts(src, dst, statBufPtr) == TCL_ERROR) {
+#ifdef ANDROID
+ if (errno == EPERM) {
+ return TCL_OK;
+ }
+#endif
/*
* The copy succeeded, but setting the permissions failed, so be in a
* consistent state, we remove the file that was created by the copy.
@@ -1203,6 +1219,11 @@ TraversalCopy(
Tcl_DStringValue(dstPtr), statBufPtr) == TCL_OK) {
return TCL_OK;
}
+#ifdef ANDROID
+ if (errno == EPERM) {
+ return TCL_OK;
+ }
+#endif
break;
}
diff --git a/unix/tclUnixInit.c b/unix/tclUnixInit.c
index 520c8e5..927b1a6 100644
--- a/unix/tclUnixInit.c
+++ b/unix/tclUnixInit.c
@@ -541,6 +541,11 @@ TclpInitLibraryPath(
*/
str = defaultLibraryDir;
+#ifdef ZIPFS_IN_TCL
+ if (Tclzipfs_Mount(NULL, NULL, NULL, NULL) == TCL_OK) {
+ str = "";
+ }
+#endif
}
if (str[0] != '\0') {
objPtr = Tcl_NewStringObj(str, -1);
diff --git a/unix/tclUnixPort.h b/unix/tclUnixPort.h
index 123abec..6558332 100644
--- a/unix/tclUnixPort.h
+++ b/unix/tclUnixPort.h
@@ -447,9 +447,11 @@ extern int gettimeofday(struct timeval *tp,
*---------------------------------------------------------------------------
*/
+#ifndef ANDROID
#ifndef L_tmpnam
# define L_tmpnam 100
#endif
+#endif
/*
*---------------------------------------------------------------------------
diff --git a/unix/tclUnixTime.c b/unix/tclUnixTime.c
index 315bcf9..19cafe6 100644
--- a/unix/tclUnixTime.c
+++ b/unix/tclUnixTime.c
@@ -529,7 +529,10 @@ static void
CleanupMemory(
ClientData ignored)
{
- ckfree(lastTZ);
+ if (lastTZ != NULL) {
+ ckfree(lastTZ);
+ lastTZ = NULL;
+ }
}
/*
diff --git a/win/Makefile.in b/win/Makefile.in
index 168da2e..13a3e0c 100644
--- a/win/Makefile.in
+++ b/win/Makefile.in
@@ -296,7 +296,8 @@ GENERIC_OBJS = \
tclUtf.$(OBJEXT) \
tclUtil.$(OBJEXT) \
tclVar.$(OBJEXT) \
- tclZlib.$(OBJEXT)
+ tclZlib.$(OBJEXT) \
+ zipfs.$(OBJEXT)
TOMMATH_OBJS = \
bncore.${OBJEXT} \
@@ -741,7 +742,7 @@ clean: cleanhelp clean-packages
distclean: distclean-packages clean
$(RM) Makefile config.status config.cache config.log tclConfig.sh \
- tcl.hpj config.status.lineno
+ tcl.hpj config.status.lineno tclsh.exe.manifest
#
# Bundled package targets