diff options
author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2015-06-21 22:29:11 (GMT) |
---|---|---|
committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2015-06-21 22:29:11 (GMT) |
commit | 39faa6c610b86034420b32d6fbb5151084f4eeb4 (patch) | |
tree | 061be7461b7f66bf06c2b0387445d1ffadac9c7f | |
parent | af46fd5b12c1265e04beeab0871ce9207e86c7a9 (diff) | |
download | tcl-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.mk | 202 | ||||
-rw-r--r-- | debian/changelog | 23 | ||||
-rw-r--r-- | debian/compat | 1 | ||||
-rw-r--r-- | debian/control | 37 | ||||
-rw-r--r-- | debian/copyright | 141 | ||||
-rwxr-xr-x | debian/rules | 131 | ||||
-rw-r--r-- | debian/sdltcl8.6-dev.dirs | 2 | ||||
-rw-r--r-- | debian/sdltcl8.6-dev.files | 2 | ||||
-rw-r--r-- | debian/sdltcl8.6-doc.files | 2 | ||||
-rw-r--r-- | debian/sdltcl8.6.files | 18 | ||||
-rw-r--r-- | debian/shlibs.local | 1 | ||||
-rw-r--r-- | generic/tclEncoding.c | 10 | ||||
-rw-r--r-- | generic/tclIOUtil.c | 74 | ||||
-rw-r--r-- | generic/tclInt.decls | 12 | ||||
-rw-r--r-- | generic/tclIntDecls.h | 19 | ||||
-rw-r--r-- | generic/tclMain.c | 146 | ||||
-rw-r--r-- | generic/tclPkgConfig.c | 20 | ||||
-rw-r--r-- | generic/tclStubInit.c | 9 | ||||
-rw-r--r-- | generic/zcrypt.h | 131 | ||||
-rw-r--r-- | generic/zipfs.c | 2867 | ||||
-rw-r--r-- | generic/zipfs.h | 43 | ||||
-rw-r--r-- | pkgs/Android.mk | 1 | ||||
-rw-r--r-- | tcl-config.mk | 60 | ||||
-rw-r--r-- | unix/Makefile.in | 11 | ||||
-rw-r--r-- | unix/tclLoadDl.c | 40 | ||||
-rw-r--r-- | unix/tclUnixFCmd.c | 21 | ||||
-rw-r--r-- | unix/tclUnixInit.c | 5 | ||||
-rw-r--r-- | unix/tclUnixPort.h | 2 | ||||
-rw-r--r-- | unix/tclUnixTime.c | 5 | ||||
-rw-r--r-- | win/Makefile.in | 5 |
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 |