summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2021-03-31 11:37:42 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2021-03-31 11:37:42 (GMT)
commitfacec4acde56111a20908c172580556aa5fe1b96 (patch)
tree41f6e54b98501e7c848f8363e14ad38def6d05d8 /generic
parent4d36f69dcdb6022174323ab248c3d17b0073e8a9 (diff)
parent7bb6d9ce572c47747256f1e1edcb4a29cd7ac279 (diff)
downloadtcl-facec4acde56111a20908c172580556aa5fe1b96.zip
tcl-facec4acde56111a20908c172580556aa5fe1b96.tar.gz
tcl-facec4acde56111a20908c172580556aa5fe1b96.tar.bz2
Merge 9.0
Diffstat (limited to 'generic')
-rw-r--r--generic/tcl.decls21
-rw-r--r--generic/tcl.h7
-rw-r--r--generic/tclCmdIL.c2
-rw-r--r--generic/tclCompExpr.c2
-rw-r--r--generic/tclConfig.c2
-rw-r--r--generic/tclDecls.h48
-rw-r--r--generic/tclEncoding.c298
-rw-r--r--generic/tclEvent.c2
-rw-r--r--generic/tclIO.c3
-rw-r--r--generic/tclIORChan.c103
-rw-r--r--generic/tclIndexObj.c4
-rw-r--r--generic/tclInt.decls19
-rw-r--r--generic/tclInt.h15
-rw-r--r--generic/tclIntDecls.h23
-rw-r--r--generic/tclLoad.c582
-rw-r--r--generic/tclParse.c2
-rw-r--r--generic/tclPkgConfig.c2
-rw-r--r--generic/tclStringObj.c8
-rw-r--r--generic/tclStubInit.c20
-rw-r--r--generic/tclTest.c4
-rw-r--r--generic/tclThreadAlloc.c2
-rw-r--r--generic/tclTomMath.decls1
-rw-r--r--generic/tclUtf.c69
-rw-r--r--generic/tclUtil.c6
-rw-r--r--generic/tclZipfs.c3035
-rw-r--r--generic/tclZlib.c2
26 files changed, 2635 insertions, 1647 deletions
diff --git a/generic/tcl.decls b/generic/tcl.decls
index cfb0b6b..8c97ca0 100644
--- a/generic/tcl.decls
+++ b/generic/tcl.decls
@@ -888,7 +888,7 @@ declare 243 {
}
# Removed in 9.0 (stub entry only)
#declare 244 {
-# void Tcl_StaticPackage(Tcl_Interp *interp, const char *pkgName,
+# void Tcl_StaticPackage(Tcl_Interp *interp, const char *prefix,
# Tcl_PackageInitProc *initProc, Tcl_PackageInitProc *safeInitProc)
#}
# Removed in 9.0 (stub entry only)
@@ -1209,7 +1209,7 @@ declare 325 {
const char *Tcl_UtfAtIndex(const char *src, size_t index)
}
declare 326 {
- int Tcl_UtfCharComplete(const char *src, size_t length)
+ int TclUtfCharComplete(const char *src, size_t length)
}
declare 327 {
size_t Tcl_UtfBackslash(const char *src, int *readPtr, char *dst)
@@ -1221,10 +1221,10 @@ declare 329 {
const char *Tcl_UtfFindLast(const char *src, int ch)
}
declare 330 {
- const char *Tcl_UtfNext(const char *src)
+ const char *TclUtfNext(const char *src)
}
declare 331 {
- const char *Tcl_UtfPrev(const char *src, const char *start)
+ const char *TclUtfPrev(const char *src, const char *start)
}
declare 332 {
int Tcl_UtfToExternal(Tcl_Interp *interp, Tcl_Encoding encoding,
@@ -2476,6 +2476,17 @@ declare 653 {
unsigned char *Tcl_GetByteArrayFromObj(Tcl_Obj *objPtr, size_t *lengthPtr)
}
+# TIP #575
+declare 654 {
+ int Tcl_UtfCharComplete(const char *src, size_t length)
+}
+declare 655 {
+ const char *Tcl_UtfNext(const char *src)
+}
+declare 656 {
+ const char *Tcl_UtfPrev(const char *src, const char *start)
+}
+
# ----- BASELINE -- FOR -- 8.7.0 ----- #
##############################################################################
@@ -2544,7 +2555,7 @@ export {
export {
void Tcl_InitSubsystems(void)
}
-export {
+export {
int TclZipfs_AppHook(int *argc, char ***argv)
}
diff --git a/generic/tcl.h b/generic/tcl.h
index c6f5e30..701f919 100644
--- a/generic/tcl.h
+++ b/generic/tcl.h
@@ -576,6 +576,11 @@ typedef void (Tcl_ServiceModeHookProc) (int mode);
typedef void *(Tcl_InitNotifierProc) (void);
typedef void (Tcl_FinalizeNotifierProc) (void *clientData);
typedef void (Tcl_MainLoopProc) (void);
+
+/* Undocumented. To be formalized by TIP #595 */
+#define Tcl_LibraryInitProc Tcl_PackageInitProc
+#define Tcl_LibraryUnloadProc Tcl_PackageUnloadProc
+#define Tcl_StaticLibrary Tcl_StaticPackage
/*
*----------------------------------------------------------------------------
@@ -2190,7 +2195,7 @@ EXTERN const char * Tcl_InitSubsystems(void);
EXTERN const char * Tcl_SetPanicProc(
TCL_NORETURN1 Tcl_PanicProc *panicProc);
EXTERN void Tcl_StaticPackage(Tcl_Interp *interp,
- const char *pkgName,
+ const char *prefix,
Tcl_PackageInitProc *initProc,
Tcl_PackageInitProc *safeInitProc);
EXTERN Tcl_ExitProc *Tcl_SetExitProc(TCL_NORETURN1 Tcl_ExitProc *proc);
diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c
index 75eb441..64ba829 100644
--- a/generic/tclCmdIL.c
+++ b/generic/tclCmdIL.c
@@ -1699,7 +1699,7 @@ InfoLoadedCmd(
} else { /* Get pkgs just in specified interp. */
packageName = TclGetString(objv[2]);
}
- return TclGetLoadedPackagesEx(interp, interpName, packageName);
+ return TclGetLoadedLibraries(interp, interpName, packageName);
}
/*
diff --git a/generic/tclCompExpr.c b/generic/tclCompExpr.c
index 847a240..8de3e7d 100644
--- a/generic/tclCompExpr.c
+++ b/generic/tclCompExpr.c
@@ -2148,7 +2148,7 @@ ParseLexeme(
if (!TclIsBareword(*start) || *start == '_') {
size_t scanned;
- if (TclUCS4Complete(start, numBytes)) {
+ if (Tcl_UtfCharComplete(start, numBytes)) {
scanned = TclUtfToUCS4(start, &ch);
} else {
char utfBytes[8];
diff --git a/generic/tclConfig.c b/generic/tclConfig.c
index 85e05e9..09b1b27 100644
--- a/generic/tclConfig.c
+++ b/generic/tclConfig.c
@@ -178,7 +178,7 @@ Tcl_RegisterConfig(
* QueryConfigObjCmd --
*
* Implementation of "::<package>::pkgconfig", the command to query
- * configuration information embedded into a binary library.
+ * configuration information embedded into a library.
*
* Results:
* A standard tcl result.
diff --git a/generic/tclDecls.h b/generic/tclDecls.h
index cdc8ed0..0338d2b 100644
--- a/generic/tclDecls.h
+++ b/generic/tclDecls.h
@@ -859,7 +859,7 @@ EXTERN int Tcl_UniCharToUtf(int ch, char *buf);
/* 325 */
EXTERN const char * Tcl_UtfAtIndex(const char *src, size_t index);
/* 326 */
-EXTERN int Tcl_UtfCharComplete(const char *src, size_t length);
+EXTERN int TclUtfCharComplete(const char *src, size_t length);
/* 327 */
EXTERN size_t Tcl_UtfBackslash(const char *src, int *readPtr,
char *dst);
@@ -868,9 +868,9 @@ EXTERN const char * Tcl_UtfFindFirst(const char *src, int ch);
/* 329 */
EXTERN const char * Tcl_UtfFindLast(const char *src, int ch);
/* 330 */
-EXTERN const char * Tcl_UtfNext(const char *src);
+EXTERN const char * TclUtfNext(const char *src);
/* 331 */
-EXTERN const char * Tcl_UtfPrev(const char *src, const char *start);
+EXTERN const char * TclUtfPrev(const char *src, const char *start);
/* 332 */
EXTERN int Tcl_UtfToExternal(Tcl_Interp *interp,
Tcl_Encoding encoding, const char *src,
@@ -1740,6 +1740,12 @@ EXTERN Tcl_UniChar * Tcl_GetUnicodeFromObj(Tcl_Obj *objPtr,
/* 653 */
EXTERN unsigned char * Tcl_GetByteArrayFromObj(Tcl_Obj *objPtr,
size_t *lengthPtr);
+/* 654 */
+EXTERN int Tcl_UtfCharComplete(const char *src, size_t length);
+/* 655 */
+EXTERN const char * Tcl_UtfNext(const char *src);
+/* 656 */
+EXTERN const char * Tcl_UtfPrev(const char *src, const char *start);
typedef struct {
const struct TclPlatStubs *tclPlatStubs;
@@ -2077,12 +2083,12 @@ typedef struct TclStubs {
int (*tcl_UniCharToUpper) (int ch); /* 323 */
int (*tcl_UniCharToUtf) (int ch, char *buf); /* 324 */
const char * (*tcl_UtfAtIndex) (const char *src, size_t index); /* 325 */
- int (*tcl_UtfCharComplete) (const char *src, size_t length); /* 326 */
+ int (*tclUtfCharComplete) (const char *src, size_t length); /* 326 */
size_t (*tcl_UtfBackslash) (const char *src, int *readPtr, char *dst); /* 327 */
const char * (*tcl_UtfFindFirst) (const char *src, int ch); /* 328 */
const char * (*tcl_UtfFindLast) (const char *src, int ch); /* 329 */
- const char * (*tcl_UtfNext) (const char *src); /* 330 */
- const char * (*tcl_UtfPrev) (const char *src, const char *start); /* 331 */
+ const char * (*tclUtfNext) (const char *src); /* 330 */
+ const char * (*tclUtfPrev) (const char *src, const char *start); /* 331 */
int (*tcl_UtfToExternal) (Tcl_Interp *interp, Tcl_Encoding encoding, const char *src, size_t srcLen, int flags, Tcl_EncodingState *statePtr, char *dst, size_t dstLen, int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr); /* 332 */
char * (*tcl_UtfToExternalDString) (Tcl_Encoding encoding, const char *src, size_t srcLen, Tcl_DString *dsPtr); /* 333 */
int (*tcl_UtfToLower) (char *src); /* 334 */
@@ -2405,6 +2411,9 @@ typedef struct TclStubs {
char * (*tcl_GetStringFromObj) (Tcl_Obj *objPtr, size_t *lengthPtr); /* 651 */
Tcl_UniChar * (*tcl_GetUnicodeFromObj) (Tcl_Obj *objPtr, size_t *lengthPtr); /* 652 */
unsigned char * (*tcl_GetByteArrayFromObj) (Tcl_Obj *objPtr, size_t *lengthPtr); /* 653 */
+ int (*tcl_UtfCharComplete) (const char *src, size_t length); /* 654 */
+ const char * (*tcl_UtfNext) (const char *src); /* 655 */
+ const char * (*tcl_UtfPrev) (const char *src, const char *start); /* 656 */
} TclStubs;
extern const TclStubs *tclStubsPtr;
@@ -3023,18 +3032,18 @@ extern const TclStubs *tclStubsPtr;
(tclStubsPtr->tcl_UniCharToUtf) /* 324 */
#define Tcl_UtfAtIndex \
(tclStubsPtr->tcl_UtfAtIndex) /* 325 */
-#define Tcl_UtfCharComplete \
- (tclStubsPtr->tcl_UtfCharComplete) /* 326 */
+#define TclUtfCharComplete \
+ (tclStubsPtr->tclUtfCharComplete) /* 326 */
#define Tcl_UtfBackslash \
(tclStubsPtr->tcl_UtfBackslash) /* 327 */
#define Tcl_UtfFindFirst \
(tclStubsPtr->tcl_UtfFindFirst) /* 328 */
#define Tcl_UtfFindLast \
(tclStubsPtr->tcl_UtfFindLast) /* 329 */
-#define Tcl_UtfNext \
- (tclStubsPtr->tcl_UtfNext) /* 330 */
-#define Tcl_UtfPrev \
- (tclStubsPtr->tcl_UtfPrev) /* 331 */
+#define TclUtfNext \
+ (tclStubsPtr->tclUtfNext) /* 330 */
+#define TclUtfPrev \
+ (tclStubsPtr->tclUtfPrev) /* 331 */
#define Tcl_UtfToExternal \
(tclStubsPtr->tcl_UtfToExternal) /* 332 */
#define Tcl_UtfToExternalDString \
@@ -3661,6 +3670,12 @@ extern const TclStubs *tclStubsPtr;
(tclStubsPtr->tcl_GetUnicodeFromObj) /* 652 */
#define Tcl_GetByteArrayFromObj \
(tclStubsPtr->tcl_GetByteArrayFromObj) /* 653 */
+#define Tcl_UtfCharComplete \
+ (tclStubsPtr->tcl_UtfCharComplete) /* 654 */
+#define Tcl_UtfNext \
+ (tclStubsPtr->tcl_UtfNext) /* 655 */
+#define Tcl_UtfPrev \
+ (tclStubsPtr->tcl_UtfPrev) /* 656 */
#endif /* defined(USE_TCL_STUBS) */
@@ -3893,11 +3908,10 @@ extern const TclStubs *tclStubsPtr;
#define Tcl_Close(interp, chan) Tcl_CloseEx(interp, chan, 0)
-#if defined(USE_TCL_STUBS) && (TCL_UTF_MAX <= 3)
-# undef Tcl_UtfCharComplete
-# define Tcl_UtfCharComplete(src, length) (((unsigned)((unsigned char)*(src) - 0xF0) < 5) \
- ? ((length) >= TCL_UTF_MAX) : tclStubsPtr->tcl_UtfCharComplete((src), (length)))
-#endif
+#undef TclUnusedStubEntry
+#undef TclUtfCharComplete
+#undef TclUtfNext
+#undef TclUtfPrev
#ifndef TCL_NO_DEPRECATED
# define Tcl_CreateSlave Tcl_CreateChild
# define Tcl_GetSlave Tcl_GetChild
diff --git a/generic/tclEncoding.c b/generic/tclEncoding.c
index 3ef4e58..8a29722 100644
--- a/generic/tclEncoding.c
+++ b/generic/tclEncoding.c
@@ -220,14 +220,7 @@ static size_t unilen(const char *src);
static Tcl_EncodingConvertProc Utf16ToUtfProc;
static Tcl_EncodingConvertProc UtfToUtf16Proc;
static Tcl_EncodingConvertProc UtfToUcs2Proc;
-static int UtfToUtfProc(ClientData clientData,
- const char *src, int srcLen, int flags,
- Tcl_EncodingState *statePtr, char *dst,
- int dstLen, int *srcReadPtr,
- int *dstWrotePtr, int *dstCharsPtr,
- int pureNullMode);
-static Tcl_EncodingConvertProc UtfIntToUtfExtProc;
-static Tcl_EncodingConvertProc UtfExtToUtfIntProc;
+static Tcl_EncodingConvertProc UtfToUtfProc;
static Tcl_EncodingConvertProc Iso88591FromUtfProc;
static Tcl_EncodingConvertProc Iso88591ToUtfProc;
@@ -517,6 +510,10 @@ FillEncodingFileMap(void)
*---------------------------------------------------------------------------
*/
+/* Those flags must not conflict with other TCL_ENCODING_* flags in tcl.h */
+#define TCL_ENCODING_MODIFIED 0x20 /* Converting NULL bytes to 0xC0 0x80 */
+#define TCL_ENCODING_LE 0x80 /* Little-endian encoding, for ucs-2/utf-16 only */
+
void
TclInitEncodingSubsystem(void)
{
@@ -525,7 +522,7 @@ TclInitEncodingSubsystem(void)
unsigned size;
unsigned short i;
union {
- char c;
+ unsigned char c;
short s;
} isLe;
@@ -533,7 +530,7 @@ TclInitEncodingSubsystem(void)
return;
}
- isLe.s = 1;
+ isLe.s = TCL_ENCODING_LE;
Tcl_MutexLock(&encodingMutex);
Tcl_InitHashTable(&encodingTable, TCL_STRING_KEYS);
Tcl_MutexUnlock(&encodingMutex);
@@ -553,8 +550,8 @@ TclInitEncodingSubsystem(void)
tclIdentityEncoding = Tcl_CreateEncoding(&type);
type.encodingName = "utf-8";
- type.toUtfProc = UtfExtToUtfIntProc;
- type.fromUtfProc = UtfIntToUtfExtProc;
+ type.toUtfProc = UtfToUtfProc;
+ type.fromUtfProc = UtfToUtfProc;
type.freeProc = NULL;
type.nullSize = 1;
type.clientData = NULL;
@@ -565,7 +562,7 @@ TclInitEncodingSubsystem(void)
type.freeProc = NULL;
type.nullSize = 2;
type.encodingName = "ucs-2le";
- type.clientData = INT2PTR(1);
+ type.clientData = INT2PTR(TCL_ENCODING_LE);
Tcl_CreateEncoding(&type);
type.encodingName = "ucs-2be";
type.clientData = INT2PTR(0);
@@ -579,7 +576,7 @@ TclInitEncodingSubsystem(void)
type.freeProc = NULL;
type.nullSize = 2;
type.encodingName = "utf-16le";
- type.clientData = INT2PTR(1);
+ type.clientData = INT2PTR(TCL_ENCODING_LE);
Tcl_CreateEncoding(&type);
type.encodingName = "utf-16be";
type.clientData = INT2PTR(0);
@@ -1077,7 +1074,7 @@ Tcl_ExternalToUtfDString(
srcLen = encodingPtr->lengthProc(src);
}
- flags = TCL_ENCODING_START | TCL_ENCODING_END;
+ flags = TCL_ENCODING_START | TCL_ENCODING_END | TCL_ENCODING_MODIFIED;
while (1) {
result = encodingPtr->toUtfProc(encodingPtr->clientData, src, srcLen,
@@ -1186,25 +1183,24 @@ Tcl_ExternalToUtf(
if (!noTerminate) {
/*
* If there are any null characters in the middle of the buffer,
- * they will converted to the UTF-8 null character (\xC080). To get
+ * they will converted to the UTF-8 null character (\xC0\x80). To get
* the actual \0 at the end of the destination buffer, we need to
* append it manually. First make room for it...
*/
dstLen--;
}
+ flags |= TCL_ENCODING_MODIFIED;
do {
- int savedFlags = flags;
Tcl_EncodingState savedState = *statePtr;
result = encodingPtr->toUtfProc(encodingPtr->clientData, src, srcLen,
- flags, statePtr, dst, dstLen, srcReadPtr, dstWrotePtr,
+ flags , statePtr, dst, dstLen, srcReadPtr, dstWrotePtr,
dstCharsPtr);
if (*dstCharsPtr <= maxChars) {
break;
}
dstLen = Tcl_UtfAtIndex(dst, maxChars) - dst + (TCL_UTF_MAX - 1);
- flags = savedFlags;
*statePtr = savedState;
} while (1);
if (!noTerminate) {
@@ -1269,8 +1265,8 @@ Tcl_UtfToExternalDString(
flags = TCL_ENCODING_START | TCL_ENCODING_END;
while (1) {
result = encodingPtr->fromUtfProc(encodingPtr->clientData, src,
- srcLen, flags, &state, dst, dstLen, &srcRead, &dstWrote,
- &dstChars);
+ srcLen, flags, &state, dst, dstLen,
+ &srcRead, &dstWrote, &dstChars);
soFar = dst + dstWrote - Tcl_DStringValue(dstPtr);
if (result != TCL_CONVERT_NOSPACE) {
@@ -1371,8 +1367,8 @@ Tcl_UtfToExternal(
dstLen -= encodingPtr->nullSize;
result = encodingPtr->fromUtfProc(encodingPtr->clientData, src, srcLen,
- flags, statePtr, dst, dstLen, srcReadPtr, dstWrotePtr,
- dstCharsPtr);
+ flags, statePtr, dst, dstLen, srcReadPtr,
+ dstWrotePtr, dstCharsPtr);
if (encodingPtr->nullSize == 2) {
dst[*dstWrotePtr + 1] = '\0';
}
@@ -2095,104 +2091,6 @@ BinaryProc(
/*
*-------------------------------------------------------------------------
*
- * UtfIntToUtfExtProc --
- *
- * Convert from UTF-8 to UTF-8. While converting null-bytes from the
- * Tcl's internal representation (0xC0, 0x80) to the official
- * representation (0x00). See UtfToUtfProc for details.
- *
- * Results:
- * Returns TCL_OK if conversion was successful.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-UtfIntToUtfExtProc(
- ClientData clientData,
- const char *src, /* Source string in UTF-8. */
- int srcLen, /* Source string length in bytes. */
- int flags, /* Conversion control flags. */
- Tcl_EncodingState *statePtr,/* Place for conversion routine to store state
- * information used during a piecewise
- * conversion. Contents of statePtr are
- * initialized and/or reset by conversion
- * routine under control of flags argument. */
- char *dst, /* Output buffer in which converted string
- * is stored. */
- int dstLen, /* The maximum length of output buffer in
- * bytes. */
- int *srcReadPtr, /* Filled with the number of bytes from the
- * source string that were converted. This may
- * be less than the original source length if
- * there was a problem converting some source
- * characters. */
- int *dstWrotePtr, /* Filled with the number of bytes that were
- * stored in the output buffer as a result of
- * the conversion. */
- int *dstCharsPtr) /* Filled with the number of characters that
- * correspond to the bytes stored in the
- * output buffer. */
-{
- return UtfToUtfProc(clientData, src, srcLen, flags, statePtr, dst, dstLen,
- srcReadPtr, dstWrotePtr, dstCharsPtr, 1);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
- * UtfExtToUtfIntProc --
- *
- * Convert from UTF-8 to UTF-8 while converting null-bytes from the
- * official representation (0x00) to Tcl's internal representation (0xC0,
- * 0x80). See UtfToUtfProc for details.
- *
- * Results:
- * Returns TCL_OK if conversion was successful.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-UtfExtToUtfIntProc(
- ClientData clientData,
- const char *src, /* Source string in UTF-8. */
- int srcLen, /* Source string length in bytes. */
- int flags, /* Conversion control flags. */
- Tcl_EncodingState *statePtr,/* Place for conversion routine to store state
- * information used during a piecewise
- * conversion. Contents of statePtr are
- * initialized and/or reset by conversion
- * routine under control of flags argument. */
- char *dst, /* Output buffer in which converted string is
- * stored. */
- int dstLen, /* The maximum length of output buffer in
- * bytes. */
- int *srcReadPtr, /* Filled with the number of bytes from the
- * source string that were converted. This may
- * be less than the original source length if
- * there was a problem converting some source
- * characters. */
- int *dstWrotePtr, /* Filled with the number of bytes that were
- * stored in the output buffer as a result of
- * the conversion. */
- int *dstCharsPtr) /* Filled with the number of characters that
- * correspond to the bytes stored in the
- * output buffer. */
-{
- return UtfToUtfProc(clientData, src, srcLen, flags, statePtr, dst, dstLen,
- srcReadPtr, dstWrotePtr, dstCharsPtr, 0);
-}
-
-/*
- *-------------------------------------------------------------------------
- *
* UtfToUtfProc --
*
* Convert from UTF-8 to UTF-8. Note that the UTF-8 to UTF-8 translation
@@ -2214,11 +2112,7 @@ UtfToUtfProc(
const char *src, /* Source string in UTF-8. */
int srcLen, /* Source string length in bytes. */
int flags, /* Conversion control flags. */
- Tcl_EncodingState *statePtr,/* Place for conversion routine to store state
- * information used during a piecewise
- * conversion. Contents of statePtr are
- * initialized and/or reset by conversion
- * routine under control of flags argument. */
+ TCL_UNUSED(Tcl_EncodingState *),
char *dst, /* Output buffer in which converted string is
* stored. */
int dstLen, /* The maximum length of output buffer in
@@ -2231,21 +2125,15 @@ UtfToUtfProc(
int *dstWrotePtr, /* Filled with the number of bytes that were
* stored in the output buffer as a result of
* the conversion. */
- int *dstCharsPtr, /* Filled with the number of characters that
+ int *dstCharsPtr) /* Filled with the number of characters that
* correspond to the bytes stored in the
* output buffer. */
- int pureNullMode) /* Convert embedded nulls from internal
- * representation to real null-bytes or vice
- * versa. Also combine or separate surrogate pairs */
{
const char *srcStart, *srcEnd, *srcClose;
const char *dstStart, *dstEnd;
int result, numChars, charLimit = INT_MAX;
- int *chPtr = (int *) statePtr;
+ int ch;
- if (flags & TCL_ENCODING_START) {
- *statePtr = 0;
- }
result = TCL_OK;
srcStart = src;
@@ -2262,7 +2150,7 @@ UtfToUtfProc(
dstEnd = dst + dstLen - TCL_UTF_MAX;
for (numChars = 0; src < srcEnd && numChars <= charLimit; numChars++) {
- if ((src > srcClose) && (!TclUCS4Complete(src, srcEnd - src))) {
+ if ((src > srcClose) && (!Tcl_UtfCharComplete(src, srcEnd - src))) {
/*
* If there is more string to follow, this will ensure that the
* last UTF-8 character in the source buffer hasn't been cut off.
@@ -2275,48 +2163,62 @@ UtfToUtfProc(
result = TCL_CONVERT_NOSPACE;
break;
}
- if (UCHAR(*src) < 0x80 && !(UCHAR(*src) == 0 && pureNullMode == 0)) {
+ if (UCHAR(*src) < 0x80 && !(UCHAR(*src) == 0 && (flags & TCL_ENCODING_MODIFIED))) {
/*
* Copy 7bit characters, but skip null-bytes when we are in input
* mode, so that they get converted to 0xC080.
*/
*dst++ = *src++;
- } else if (pureNullMode == 1 && UCHAR(*src) == 0xC0 &&
- (src + 1 < srcEnd) && UCHAR(*(src+1)) == 0x80) {
+ } else if (UCHAR(*src) == 0xC0 && (src + 1 < srcEnd)
+ && UCHAR(src[1]) == 0x80 && !(flags & TCL_ENCODING_MODIFIED)) {
/*
* Convert 0xC080 to real nulls when we are in output mode.
*/
*dst++ = 0;
src += 2;
- } else if (!TclUCS4Complete(src, srcEnd - src)) {
+ } else if (!Tcl_UtfCharComplete(src, srcEnd - src)) {
/*
* Always check before using TclUtfToUCS4. Not doing can so
* cause it run beyond the end of the buffer! If we happen such an
- * incomplete char its bytes are made to represent themselves.
+ * incomplete char its bytes are made to represent themselves
+ * unless the user has explicitly asked to be told.
*/
- *chPtr = UCHAR(*src);
+ if (flags & TCL_ENCODING_STOPONERROR) {
+ result = TCL_CONVERT_MULTIBYTE;
+ break;
+ }
+ ch = UCHAR(*src);
src += 1;
- dst += Tcl_UniCharToUtf(*chPtr, dst);
+ dst += Tcl_UniCharToUtf(ch, dst);
} else {
- src += TclUtfToUCS4(src, chPtr);
- if ((*chPtr | 0x7FF) == 0xDFFF) {
- /* A surrogate character is detected, handle especially */
- int low = *chPtr;
- size_t len = (src <= srcEnd-3) ? TclUtfToUCS4(src, &low) : 0;
- if (((low & ~0x3FF) != 0xDC00) || (*chPtr & 0x400)) {
- *dst++ = (char) (((*chPtr >> 12) | 0xE0) & 0xEF);
- *dst++ = (char) (((*chPtr >> 6) | 0x80) & 0xBF);
- *dst++ = (char) ((*chPtr | 0x80) & 0xBF);
- continue;
+ size_t len = TclUtfToUCS4(src, &ch);
+ if ((len < 2) && (ch != 0) && (flags & TCL_ENCODING_STOPONERROR)) {
+ result = TCL_CONVERT_SYNTAX;
+ break;
+ }
+ src += len;
+ if ((ch | 0x7FF) == 0xDFFF) {
+ /*
+ * A surrogate character is detected, handle especially.
+ */
+
+ int low = ch;
+ len = (src <= srcEnd-3) ? TclUtfToUCS4(src, &low) : 0;
+
+ if (((low & ~0x3FF) != 0xDC00) || (ch & 0x400)) {
+ *dst++ = (char) (((ch >> 12) | 0xE0) & 0xEF);
+ *dst++ = (char) (((ch >> 6) | 0x80) & 0xBF);
+ *dst++ = (char) ((ch | 0x80) & 0xBF);
+ continue;
}
src += len;
- dst += Tcl_UniCharToUtf(*chPtr, dst);
- *chPtr = low;
+ dst += Tcl_UniCharToUtf(ch, dst);
+ ch = low;
}
- dst += Tcl_UniCharToUtf(*chPtr, dst);
+ dst += Tcl_UniCharToUtf(ch, dst);
}
}
@@ -2344,7 +2246,7 @@ UtfToUtfProc(
static int
Utf16ToUtfProc(
- ClientData clientData, /* != NULL means LE, == NUL means BE */
+ ClientData clientData, /* additional flags, e.g. TCL_ENCODING_LE */
const char *src, /* Source string in Unicode. */
int srcLen, /* Source string length in bytes. */
int flags, /* Conversion control flags. */
@@ -2370,18 +2272,27 @@ Utf16ToUtfProc(
int result, numChars, charLimit = INT_MAX;
unsigned short ch;
+ flags |= PTR2INT(clientData);
if (flags & TCL_ENCODING_CHAR_LIMIT) {
charLimit = *dstCharsPtr;
}
result = TCL_OK;
- /* check alignment with utf-16 (2 == sizeof(UTF-16)) */
+ /*
+ * Check alignment with utf-16 (2 == sizeof(UTF-16))
+ */
+
if ((srcLen % 2) != 0) {
result = TCL_CONVERT_MULTIBYTE;
srcLen--;
}
- /* If last code point is a high surrogate, we cannot handle that yet */
- if ((srcLen >= 2) && ((src[srcLen - (clientData?1:2)] & 0xFC) == 0xD8)) {
+
+ /*
+ * If last code point is a high surrogate, we cannot handle that yet.
+ */
+
+ if ((srcLen >= 2) &&
+ ((src[srcLen - ((flags & TCL_ENCODING_LE)?1:2)] & 0xFC) == 0xD8)) {
result = TCL_CONVERT_MULTIBYTE;
srcLen-= 2;
}
@@ -2398,15 +2309,17 @@ Utf16ToUtfProc(
break;
}
- if (clientData) {
+ if (flags & TCL_ENCODING_LE) {
ch = (src[1] & 0xFF) << 8 | (src[0] & 0xFF);
} else {
ch = (src[0] & 0xFF) << 8 | (src[1] & 0xFF);
}
+
/*
* Special case for 1-byte utf chars for speed. Make sure we work with
* unsigned short-size data.
*/
+
if (ch && ch < 0x80) {
*dst++ = (ch & 0xFF);
} else {
@@ -2443,11 +2356,7 @@ UtfToUtf16Proc(
const char *src, /* Source string in UTF-8. */
int srcLen, /* Source string length in bytes. */
int flags, /* Conversion control flags. */
- Tcl_EncodingState *statePtr,/* Place for conversion routine to store state
- * information used during a piecewise
- * conversion. Contents of statePtr are
- * initialized and/or reset by conversion
- * routine under control of flags argument. */
+ TCL_UNUSED(Tcl_EncodingState *),
char *dst, /* Output buffer in which converted string is
* stored. */
int dstLen, /* The maximum length of output buffer in
@@ -2466,11 +2375,8 @@ UtfToUtf16Proc(
{
const char *srcStart, *srcEnd, *srcClose, *dstStart, *dstEnd;
int result, numChars;
- Tcl_UniChar *chPtr = (Tcl_UniChar *) statePtr;
+ int ch;
- if (flags & TCL_ENCODING_START) {
- *statePtr = 0;
- }
srcStart = src;
srcEnd = src + srcLen;
srcClose = srcEnd;
@@ -2496,38 +2402,27 @@ UtfToUtf16Proc(
result = TCL_CONVERT_NOSPACE;
break;
}
- src += TclUtfToUniChar(src, chPtr);
-
+ src += TclUtfToUCS4(src, &ch);
if (clientData) {
-#if TCL_UTF_MAX > 3
- if (*chPtr <= 0xFFFF) {
- *dst++ = (*chPtr & 0xFF);
- *dst++ = (*chPtr >> 8);
+ if (ch <= 0xFFFF) {
+ *dst++ = (ch & 0xFF);
+ *dst++ = (ch >> 8);
} else {
- *dst++ = (((*chPtr - 0x10000) >> 10) & 0xFF);
- *dst++ = (((*chPtr - 0x10000) >> 18) & 0x3) | 0xD8;
- *dst++ = (*chPtr & 0xFF);
- *dst++ = ((*chPtr >> 8) & 0x3) | 0xDC;
+ *dst++ = (((ch - 0x10000) >> 10) & 0xFF);
+ *dst++ = (((ch - 0x10000) >> 18) & 0x3) | 0xD8;
+ *dst++ = (ch & 0xFF);
+ *dst++ = ((ch >> 8) & 0x3) | 0xDC;
}
-#else
- *dst++ = (*chPtr & 0xFF);
- *dst++ = (*chPtr >> 8);
-#endif
} else {
-#if TCL_UTF_MAX > 3
- if (*chPtr <= 0xFFFF) {
- *dst++ = (*chPtr >> 8);
- *dst++ = (*chPtr & 0xFF);
+ if (ch <= 0xFFFF) {
+ *dst++ = (ch >> 8);
+ *dst++ = (ch & 0xFF);
} else {
- *dst++ = (((*chPtr - 0x10000) >> 18) & 0x3) | 0xD8;
- *dst++ = (((*chPtr - 0x10000) >> 10) & 0xFF);
- *dst++ = ((*chPtr >> 8) & 0x3) | 0xDC;
- *dst++ = (*chPtr & 0xFF);
+ *dst++ = (((ch - 0x10000) >> 18) & 0x3) | 0xD8;
+ *dst++ = (((ch - 0x10000) >> 10) & 0xFF);
+ *dst++ = ((ch >> 8) & 0x3) | 0xDC;
+ *dst++ = (ch & 0xFF);
}
-#else
- *dst++ = (*chPtr >> 8);
- *dst++ = (*chPtr & 0xFF);
-#endif
}
}
*srcReadPtr = src - srcStart;
@@ -2554,7 +2449,7 @@ UtfToUtf16Proc(
static int
UtfToUcs2Proc(
- ClientData clientData, /* != NULL means LE, == NUL means BE */
+ ClientData clientData, /* additional flags, e.g. TCL_ENCODING_LE */
const char *src, /* Source string in UTF-8. */
int srcLen, /* Source string length in bytes. */
int flags, /* Conversion control flags. */
@@ -2582,6 +2477,7 @@ UtfToUcs2Proc(
#endif
Tcl_UniChar ch = 0;
+ flags |= PTR2INT(clientData);
srcStart = src;
srcEnd = src + srcLen;
srcClose = srcEnd;
@@ -2625,7 +2521,7 @@ UtfToUcs2Proc(
* casting dst to a Tcl_UniChar. [Bug 1122671]
*/
- if (clientData) {
+ if (flags & TCL_ENCODING_LE) {
*dst++ = (ch & 0xFF);
*dst++ = (ch >> 8);
} else {
@@ -3033,7 +2929,9 @@ Iso88591FromUtfProc(
break;
}
#if TCL_UTF_MAX <= 3
- if ((ch >= 0xD800) && (len < 3)) len = 4;
+ if ((ch >= 0xD800) && (len < 3)) {
+ len = 4;
+ }
#endif
/*
* Plunge on, using '?' as a fallback character.
@@ -3078,7 +2976,7 @@ TableFreeProc(
ClientData clientData) /* TableEncodingData that specifies
* encoding. */
{
- TableEncodingData *dataPtr = (TableEncodingData *)clientData;
+ TableEncodingData *dataPtr = (TableEncodingData *) clientData;
/*
* Make sure we aren't freeing twice on shutdown. [Bug 219314]
@@ -3136,7 +3034,7 @@ EscapeToUtfProc(
* correspond to the bytes stored in the
* output buffer. */
{
- EscapeEncodingData *dataPtr = (EscapeEncodingData *)clientData;
+ EscapeEncodingData *dataPtr = (EscapeEncodingData *) clientData;
const char *prefixBytes, *tablePrefixBytes, *srcStart, *srcEnd;
const unsigned short *const *tableToUnicode;
const Encoding *encodingPtr;
diff --git a/generic/tclEvent.c b/generic/tclEvent.c
index 9d287ac..699e4d3 100644
--- a/generic/tclEvent.c
+++ b/generic/tclEvent.c
@@ -1512,7 +1512,7 @@ Tcl_UpdateObjCmd(
}
switch ((enum updateOptionsEnum) optionIndex) {
case OPT_IDLETASKS:
- flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT;
+ flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
break;
default:
Tcl_Panic("Tcl_UpdateObjCmd: bad option index to UpdateOptions");
diff --git a/generic/tclIO.c b/generic/tclIO.c
index 1c4279e..30e2f74 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -3361,7 +3361,8 @@ int
TclClose(
Tcl_Interp *interp, /* Interpreter for errors. */
Tcl_Channel chan) /* The channel being closed. Must not be
- * referenced in any interpreter. */
+ * referenced in any interpreter. May be NULL,
+ * in which case this is a no-op. */
{
CloseCallback *cbPtr; /* Iterate over close callbacks for this
* channel. */
diff --git a/generic/tclIORChan.c b/generic/tclIORChan.c
index fe6d1c1..2f29816 100644
--- a/generic/tclIORChan.c
+++ b/generic/tclIORChan.c
@@ -52,12 +52,13 @@ static int ReflectGetOption(ClientData clientData,
static int ReflectSetOption(ClientData clientData,
Tcl_Interp *interp, const char *optionName,
const char *newValue);
+static int ReflectTruncate(ClientData clientData,
+ long long length);
static void TimerRunRead(ClientData clientData);
static void TimerRunWrite(ClientData clientData);
/*
- * The C layer channel type/driver definition used by the reflection. This is
- * a version 3 structure.
+ * The C layer channel type/driver definition used by the reflection.
*/
static const Tcl_ChannelType tclRChannelType = {
@@ -81,7 +82,7 @@ static const Tcl_ChannelType tclRChannelType = {
#else
NULL, /* thread action */
#endif
- NULL /* truncate */
+ ReflectTruncate /* Truncate. NULL'able */
};
/*
@@ -179,6 +180,7 @@ static const char *const methodNames[] = {
"initialize", /* */
"read", /* OPT */
"seek", /* OPT */
+ "truncate", /* OPT */
"watch", /* */
"write", /* OPT */
NULL
@@ -192,6 +194,7 @@ typedef enum {
METH_INIT,
METH_READ,
METH_SEEK,
+ METH_TRUNCATE,
METH_WATCH,
METH_WRITE
} MethodName;
@@ -201,7 +204,8 @@ typedef enum {
(FLAG(METH_INIT) | FLAG(METH_FINAL) | FLAG(METH_WATCH))
#define NULLABLE_METHODS \
(FLAG(METH_BLOCKING) | FLAG(METH_SEEK) | \
- FLAG(METH_CONFIGURE) | FLAG(METH_CGET) | FLAG(METH_CGETALL))
+ FLAG(METH_CONFIGURE) | FLAG(METH_CGET) | \
+ FLAG(METH_CGETALL) | FLAG(METH_TRUNCATE))
#define RANDW \
(TCL_READABLE | TCL_WRITABLE)
@@ -231,7 +235,8 @@ typedef enum {
ForwardedBlock,
ForwardedSetOpt,
ForwardedGetOpt,
- ForwardedGetOptAll
+ ForwardedGetOptAll,
+ ForwardedTruncate
} ForwardedOperation;
/*
@@ -294,6 +299,10 @@ struct ForwardParamGetOpt {
const char *name; /* Name of option to get, maybe NULL */
Tcl_DString *value; /* Result */
};
+struct ForwardParamTruncate {
+ ForwardParamBase base; /* "Supertype". MUST COME FIRST. */
+ Tcl_WideInt length; /* I: Length of file. */
+};
/*
* Now join all these together in a single union for convenience.
@@ -308,6 +317,7 @@ typedef union ForwardParam {
struct ForwardParamBlock block;
struct ForwardParamSetOpt setOpt;
struct ForwardParamGetOpt getOpt;
+ struct ForwardParamTruncate truncate;
} ForwardParam;
/*
@@ -695,6 +705,9 @@ TclChanCreateObjCmd(
if (!(methods & FLAG(METH_SEEK))) {
clonePtr->wideSeekProc = NULL;
}
+ if (!(methods & FLAG(METH_TRUNCATE))) {
+ clonePtr->truncateProc = NULL;
+ }
chanPtr->typePtr = clonePtr;
}
@@ -2017,6 +2030,73 @@ ReflectGetOption(
}
/*
+ *----------------------------------------------------------------------
+ *
+ * ReflectTruncate --
+ *
+ * This function is invoked to truncate a channel's file size.
+ *
+ * Results:
+ * A standard Tcl result code.
+ *
+ * Side effects:
+ * Arbitrary, as it calls upon a Tcl script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ReflectTruncate(
+ ClientData clientData, /* Channel to query */
+ long long length) /* Length to truncate to. */
+{
+ ReflectedChannel *rcPtr = (ReflectedChannel *)clientData;
+ Tcl_Obj *lenObj;
+ int errorNum; /* EINVAL or EOK (success). */
+ Tcl_Obj *resObj; /* Result for 'truncate' */
+
+ /*
+ * Are we in the correct thread?
+ */
+
+#ifdef TCL_THREADS
+ if (rcPtr->thread != Tcl_GetCurrentThread()) {
+ ForwardParam p;
+
+ p.truncate.length = length;
+
+ ForwardOpToHandlerThread(rcPtr, ForwardedTruncate, &p);
+
+ if (p.base.code != TCL_OK) {
+ PassReceivedError(rcPtr->chan, &p);
+ return EINVAL;
+ }
+
+ return EOK;
+ }
+#endif
+
+ /* ASSERT: rcPtr->method & FLAG(METH_TRUNCATE) */
+
+ Tcl_Preserve(rcPtr);
+
+ lenObj = Tcl_NewIntObj(length);
+ Tcl_IncrRefCount(lenObj);
+
+ if (InvokeTclMethod(rcPtr,METH_TRUNCATE,lenObj,NULL,&resObj)!=TCL_OK) {
+ Tcl_SetChannelError(rcPtr->chan, resObj);
+ errorNum = EINVAL;
+ } else {
+ errorNum = EOK;
+ }
+
+ Tcl_DecrRefCount(lenObj);
+ Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */
+ Tcl_Release(rcPtr);
+ return errorNum;
+}
+
+/*
* Helpers. =========================================================
*/
@@ -3247,6 +3327,19 @@ ForwardProc(
Tcl_Release(rcPtr);
break;
+ case ForwardedTruncate: {
+ Tcl_Obj *lenObj = Tcl_NewIntObj(paramPtr->truncate.length);
+
+ Tcl_IncrRefCount(lenObj);
+ Tcl_Preserve(rcPtr);
+ if (InvokeTclMethod(rcPtr,METH_TRUNCATE,lenObj,NULL,&resObj)!=TCL_OK) {
+ ForwardSetObjError(paramPtr, resObj);
+ }
+ Tcl_Release(rcPtr);
+ Tcl_DecrRefCount(lenObj);
+ break;
+ }
+
default:
/*
* Bad operation code.
diff --git a/generic/tclIndexObj.c b/generic/tclIndexObj.c
index 02d0e31..b665589 100644
--- a/generic/tclIndexObj.c
+++ b/generic/tclIndexObj.c
@@ -718,7 +718,7 @@ PrefixLongestObjCmd(
* Adjust in case we stopped in the middle of a UTF char.
*/
- resultLength = TclUtfPrev(&resultString[i+1],
+ resultLength = Tcl_UtfPrev(&resultString[i+1],
resultString) - resultString;
break;
}
@@ -1237,7 +1237,6 @@ PrintUsage(
int width, numSpaces;
#define NUM_SPACES 20
static const char spaces[] = " ";
- char tmp[TCL_DOUBLE_SPACE];
Tcl_Obj *msg;
/*
@@ -1287,7 +1286,6 @@ PrintUsage(
case TCL_ARGV_FLOAT:
Tcl_AppendPrintfToObj(msg, "\n\t\tDefault value: %g",
*((double *) infoPtr->dstPtr));
- sprintf(tmp, "%g", *((double *) infoPtr->dstPtr));
break;
case TCL_ARGV_STRING: {
char *string = *((char **) infoPtr->dstPtr);
diff --git a/generic/tclInt.decls b/generic/tclInt.decls
index 3fbc571..99d2aaa 100644
--- a/generic/tclInt.decls
+++ b/generic/tclInt.decls
@@ -17,6 +17,7 @@ library tcl
# Define the unsupported generic interfaces.
interface tclInt
+scspec EXTERN
# Declare each of the functions in the unsupported internal Tcl
# interface. These interfaces are allowed to changed between versions.
@@ -467,6 +468,7 @@ declare 232 {
declare 233 {
void TclGetSrcInfoForPc(CmdFrame *contextPtr)
}
+
# Exports for VarReform compat: Itcl, XOTcl like to peek into our varTables :(
declare 234 {
Var *TclVarHashCreateVar(TclVarHashTable *tablePtr, const char *key,
@@ -475,10 +477,17 @@ declare 234 {
declare 235 {
void TclInitVarHashTable(TclVarHashTable *tablePtr, Namespace *nsPtr)
}
+# TIP 542
+declare 236 {
+ void TclAppendUnicodeToObj(Tcl_Obj *objPtr,
+ const Tcl_UniChar *unicode, size_t length)
+}
+
# TIP #285: Script cancellation support.
declare 237 {
int TclResetCancellation(Tcl_Interp *interp, int force)
}
+
# NRE functions for "rogue" extensions to exploit NRE; they will need to
# include NRE.h too.
declare 238 {
@@ -567,9 +576,8 @@ declare 256 {
int TclPtrUnsetVar(Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr,
Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, const int flags)
}
-
declare 257 {
- void TclStaticPackage(Tcl_Interp *interp, const char *pkgName,
+ void TclStaticPackage(Tcl_Interp *interp, const char *prefix,
Tcl_PackageInitProc *initProc, Tcl_PackageInitProc *safeInitProc)
}
@@ -578,13 +586,8 @@ declare 258 {
Tcl_Obj *TclpCreateTemporaryDirectory(Tcl_Obj *dirObj,
Tcl_Obj *basenameObj)
}
-# TIP 542
-declare 259 {
- void TclAppendUnicodeToObj(Tcl_Obj *objPtr,
- const Tcl_UniChar *unicode, size_t length)
-}
-declare 260 {
+declare 259 {
unsigned char *TclGetBytesFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
size_t *lengthPtr)
}
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 0c02f4c..2448b5a 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2975,7 +2975,7 @@ MODULE_SCOPE Tcl_Obj * TclGetSourceFromFrame(CmdFrame *cfPtr, int objc,
Tcl_Obj *const objv[]);
MODULE_SCOPE char * TclGetStringStorage(Tcl_Obj *objPtr,
size_t *sizePtr);
-MODULE_SCOPE int TclGetLoadedPackagesEx(Tcl_Interp *interp,
+MODULE_SCOPE int TclGetLoadedLibraries(Tcl_Interp *interp,
const char *targetName,
const char *packageName);
MODULE_SCOPE int TclGetWideBitsFromObj(Tcl_Interp *, Tcl_Obj *,
@@ -3184,16 +3184,10 @@ MODULE_SCOPE size_t TclUtfCount(int ch);
# define TclUtfToUCS4 Tcl_UtfToUniChar
# define TclUniCharToUCS4(src, ptr) (*ptr = *(src),1)
# define TclUCS4Prev(src, ptr) (((src) > (ptr)) ? ((src) - 1) : (src))
-# define TclUCS4Complete Tcl_UtfCharComplete
-# define TclChar16Complete(src, length) (((unsigned)((unsigned char)*(src) - 0xF0) < 5) \
- ? ((length) >= 3) : Tcl_UtfCharComplete((src), (length)))
#else
MODULE_SCOPE int TclUtfToUCS4(const char *, int *);
MODULE_SCOPE int TclUniCharToUCS4(const Tcl_UniChar *, int *);
MODULE_SCOPE const Tcl_UniChar *TclUCS4Prev(const Tcl_UniChar *, const Tcl_UniChar *);
-# define TclUCS4Complete(src, length) (((unsigned)((unsigned char)*(src) - 0xF0) < 5) \
- ? ((length) >= 4) : Tcl_UtfCharComplete((src), (length)))
-# define TclChar16Complete Tcl_UtfCharComplete
#endif
MODULE_SCOPE Tcl_Obj * TclpNativeToNormalized(void *clientData);
MODULE_SCOPE Tcl_Obj * TclpFilesystemPathType(Tcl_Obj *pathPtr);
@@ -4629,11 +4623,6 @@ MODULE_SCOPE const TclFileAttrProcs tclpFileAttrProcs[];
(numChars) = _count; \
} while (0);
-#define TclUtfPrev(src, start) \
- (((src) < (start) + 2) ? (start) : \
- ((unsigned char) *((src) - 1)) < 0x80 ? (src) - 1 : \
- Tcl_UtfPrev(src, start))
-
/*
*----------------------------------------------------------------
* Macro that encapsulates the logic that determines when it is safe to
@@ -4670,7 +4659,7 @@ MODULE_SCOPE int TclIsPureByteArray(Tcl_Obj *objPtr);
*----------------------------------------------------------------
*/
-#ifdef WORDS_BIGENDIAN
+#if defined(WORDS_BIGENDIAN) && (TCL_UTF_MAX > 3)
# define TclUniCharNcmp(cs,ct,n) memcmp((cs),(ct),(n)*sizeof(Tcl_UniChar))
#endif /* WORDS_BIGENDIAN */
diff --git a/generic/tclIntDecls.h b/generic/tclIntDecls.h
index 23cf3e6..e57b295 100644
--- a/generic/tclIntDecls.h
+++ b/generic/tclIntDecls.h
@@ -501,7 +501,9 @@ EXTERN Var * TclVarHashCreateVar(TclVarHashTable *tablePtr,
/* 235 */
EXTERN void TclInitVarHashTable(TclVarHashTable *tablePtr,
Namespace *nsPtr);
-/* Slot 236 is reserved */
+/* 236 */
+EXTERN void TclAppendUnicodeToObj(Tcl_Obj *objPtr,
+ const Tcl_UniChar *unicode, size_t length);
/* 237 */
EXTERN int TclResetCancellation(Tcl_Interp *interp, int force);
/* 238 */
@@ -571,16 +573,13 @@ EXTERN int TclPtrUnsetVar(Tcl_Interp *interp, Tcl_Var varPtr,
Tcl_Obj *part2Ptr, const int flags);
/* 257 */
EXTERN void TclStaticPackage(Tcl_Interp *interp,
- const char *pkgName,
+ const char *prefix,
Tcl_PackageInitProc *initProc,
Tcl_PackageInitProc *safeInitProc);
/* 258 */
EXTERN Tcl_Obj * TclpCreateTemporaryDirectory(Tcl_Obj *dirObj,
Tcl_Obj *basenameObj);
/* 259 */
-EXTERN void TclAppendUnicodeToObj(Tcl_Obj *objPtr,
- const Tcl_UniChar *unicode, size_t length);
-/* 260 */
EXTERN unsigned char * TclGetBytesFromObj(Tcl_Interp *interp,
Tcl_Obj *objPtr, size_t *lengthPtr);
@@ -824,7 +823,7 @@ typedef struct TclIntStubs {
void (*tclGetSrcInfoForPc) (CmdFrame *contextPtr); /* 233 */
Var * (*tclVarHashCreateVar) (TclVarHashTable *tablePtr, const char *key, int *newPtr); /* 234 */
void (*tclInitVarHashTable) (TclVarHashTable *tablePtr, Namespace *nsPtr); /* 235 */
- void (*reserved236)(void);
+ void (*tclAppendUnicodeToObj) (Tcl_Obj *objPtr, const Tcl_UniChar *unicode, size_t length); /* 236 */
int (*tclResetCancellation) (Tcl_Interp *interp, int force); /* 237 */
int (*tclNRInterpProc) (void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); /* 238 */
int (*tclNRInterpProcCore) (Tcl_Interp *interp, Tcl_Obj *procNameObj, int skip, ProcErrorProc *errorProc); /* 239 */
@@ -845,10 +844,9 @@ typedef struct TclIntStubs {
Tcl_Obj * (*tclPtrIncrObjVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *incrPtr, const int flags); /* 254 */
int (*tclPtrObjMakeUpvar) (Tcl_Interp *interp, Tcl_Var otherPtr, Tcl_Obj *myNamePtr, int myFlags); /* 255 */
int (*tclPtrUnsetVar) (Tcl_Interp *interp, Tcl_Var varPtr, Tcl_Var arrayPtr, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, const int flags); /* 256 */
- void (*tclStaticPackage) (Tcl_Interp *interp, const char *pkgName, Tcl_PackageInitProc *initProc, Tcl_PackageInitProc *safeInitProc); /* 257 */
+ void (*tclStaticPackage) (Tcl_Interp *interp, const char *prefix, Tcl_PackageInitProc *initProc, Tcl_PackageInitProc *safeInitProc); /* 257 */
Tcl_Obj * (*tclpCreateTemporaryDirectory) (Tcl_Obj *dirObj, Tcl_Obj *basenameObj); /* 258 */
- void (*tclAppendUnicodeToObj) (Tcl_Obj *objPtr, const Tcl_UniChar *unicode, size_t length); /* 259 */
- unsigned char * (*tclGetBytesFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, size_t *lengthPtr); /* 260 */
+ unsigned char * (*tclGetBytesFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, size_t *lengthPtr); /* 259 */
} TclIntStubs;
extern const TclIntStubs *tclIntStubsPtr;
@@ -1219,7 +1217,8 @@ extern const TclIntStubs *tclIntStubsPtr;
(tclIntStubsPtr->tclVarHashCreateVar) /* 234 */
#define TclInitVarHashTable \
(tclIntStubsPtr->tclInitVarHashTable) /* 235 */
-/* Slot 236 is reserved */
+#define TclAppendUnicodeToObj \
+ (tclIntStubsPtr->tclAppendUnicodeToObj) /* 236 */
#define TclResetCancellation \
(tclIntStubsPtr->tclResetCancellation) /* 237 */
#define TclNRInterpProc \
@@ -1264,10 +1263,8 @@ extern const TclIntStubs *tclIntStubsPtr;
(tclIntStubsPtr->tclStaticPackage) /* 257 */
#define TclpCreateTemporaryDirectory \
(tclIntStubsPtr->tclpCreateTemporaryDirectory) /* 258 */
-#define TclAppendUnicodeToObj \
- (tclIntStubsPtr->tclAppendUnicodeToObj) /* 259 */
#define TclGetBytesFromObj \
- (tclIntStubsPtr->tclGetBytesFromObj) /* 260 */
+ (tclIntStubsPtr->tclGetBytesFromObj) /* 259 */
#endif /* defined(USE_TCL_STUBS) */
diff --git a/generic/tclLoad.c b/generic/tclLoad.c
index 364e97b..8c163b7 100644
--- a/generic/tclLoad.c
+++ b/generic/tclLoad.c
@@ -13,21 +13,21 @@
#include "tclInt.h"
/*
- * The following structure describes a package that has been loaded either
+ * The following structure describes a library that has been loaded either
* dynamically (with the "load" command) or statically (as indicated by a call
- * to TclGetLoadedPackages). All such packages are linked together into a
- * single list for the process. Packages are never unloaded, until the
+ * to Tcl_StaticPackage). All such libraries are linked together into a
+ * single list for the process. Library are never unloaded, until the
* application exits, when TclFinalizeLoad is called, and these structures are
* freed.
*/
-typedef struct LoadedPackage {
- char *fileName; /* Name of the file from which the package was
- * loaded. An empty string means the package
+typedef struct LoadedLibrary {
+ char *fileName; /* Name of the file from which the library was
+ * loaded. An empty string means the library
* is loaded statically. Malloc-ed. */
- char *packageName; /* Name of package prefix for the package,
+ char *prefix; /* Prefix for the library,
* properly capitalized (first letter UC,
- * others LC), no "_", as in "Net".
+ * others LC), as in "Net".
* Malloc-ed. */
Tcl_LoadHandle loadHandle; /* Token for the loaded file which should be
* passed to (*unLoadProcPtr)() when the file
@@ -35,59 +35,59 @@ typedef struct LoadedPackage {
* then this field is irrelevant. */
Tcl_PackageInitProc *initProc;
/* Initialization function to call to
- * incorporate this package into a trusted
+ * incorporate this library into a trusted
* interpreter. */
Tcl_PackageInitProc *safeInitProc;
/* Initialization function to call to
- * incorporate this package into a safe
+ * incorporate this library into a safe
* interpreter (one that will execute
- * untrusted scripts). NULL means the package
+ * untrusted scripts). NULL means the library
* can't be used in unsafe interpreters. */
Tcl_PackageUnloadProc *unloadProc;
- /* Finalisation function to unload a package
+ /* Finalization function to unload a library
* from a trusted interpreter. NULL means that
- * the package cannot be unloaded. */
+ * the library cannot be unloaded. */
Tcl_PackageUnloadProc *safeUnloadProc;
- /* Finalisation function to unload a package
+ /* Finalization function to unload a library
* from a safe interpreter. NULL means that
- * the package cannot be unloaded. */
- int interpRefCount; /* How many times the package has been loaded
+ * the library cannot be unloaded. */
+ int interpRefCount; /* How many times the library has been loaded
* in trusted interpreters. */
- int safeInterpRefCount; /* How many times the package has been loaded
+ int safeInterpRefCount; /* How many times the library has been loaded
* in safe interpreters. */
- struct LoadedPackage *nextPtr;
- /* Next in list of all packages loaded into
+ struct LoadedLibrary *nextPtr;
+ /* Next in list of all libraries loaded into
* this application process. NULL means end of
* list. */
-} LoadedPackage;
+} LoadedLibrary;
/*
* TCL_THREADS
- * There is a global list of packages that is anchored at firstPackagePtr.
+ * There is a global list of libraries that is anchored at firstLibraryPtr.
* Access to this list is governed by a mutex.
*/
-static LoadedPackage *firstPackagePtr = NULL;
- /* First in list of all packages loaded into
+static LoadedLibrary *firstLibraryPtr = NULL;
+ /* First in list of all libraries loaded into
* this process. */
-TCL_DECLARE_MUTEX(packageMutex)
+TCL_DECLARE_MUTEX(libraryMutex)
/*
- * The following structure represents a particular package that has been
+ * The following structure represents a particular library that has been
* incorporated into a particular interpreter (by calling its initialization
* function). There is a list of these structures for each interpreter, with
* an AssocData value (key "load") for the interpreter that points to the
- * first package (if any).
+ * first library (if any).
*/
-typedef struct InterpPackage {
- LoadedPackage *pkgPtr; /* Points to detailed information about
- * package. */
- struct InterpPackage *nextPtr;
- /* Next package in this interpreter, or NULL
+typedef struct InterpLibrary {
+ LoadedLibrary *libraryPtr; /* Points to detailed information about
+ * library. */
+ struct InterpLibrary *nextPtr;
+ /* Next library in this interpreter, or NULL
* for end of list. */
-} InterpPackage;
+} InterpLibrary;
/*
* Prototypes for functions that are private to this file:
@@ -121,14 +121,14 @@ Tcl_LoadObjCmd(
Tcl_Obj *const objv[]) /* Argument objects. */
{
Tcl_Interp *target;
- LoadedPackage *pkgPtr, *defaultPtr;
- Tcl_DString pkgName, tmp, initName, safeInitName;
+ LoadedLibrary *libraryPtr, *defaultPtr;
+ Tcl_DString pfx, tmp, initName, safeInitName;
Tcl_DString unloadName, safeUnloadName;
- InterpPackage *ipFirstPtr, *ipPtr;
+ InterpLibrary *ipFirstPtr, *ipPtr;
int code, namesMatch, filesMatch, offset;
const char *symbols[2];
Tcl_PackageInitProc *initProc;
- const char *p, *fullFileName, *packageName;
+ const char *p, *fullFileName, *prefix;
Tcl_LoadHandle loadHandle;
Tcl_UniChar ch = 0;
size_t len;
@@ -159,7 +159,7 @@ Tcl_LoadObjCmd(
}
}
if ((objc < 2) || (objc > 4)) {
- Tcl_WrongNumArgs(interp, 1, savedobjv, "?-global? ?-lazy? ?--? fileName ?packageName? ?interp?");
+ Tcl_WrongNumArgs(interp, 1, savedobjv, "?-global? ?-lazy? ?--? fileName ?prefix? ?interp?");
return TCL_ERROR;
}
if (Tcl_FSConvertToPathType(interp, objv[1]) != TCL_OK) {
@@ -167,23 +167,23 @@ Tcl_LoadObjCmd(
}
fullFileName = TclGetString(objv[1]);
- Tcl_DStringInit(&pkgName);
+ Tcl_DStringInit(&pfx);
Tcl_DStringInit(&initName);
Tcl_DStringInit(&safeInitName);
Tcl_DStringInit(&unloadName);
Tcl_DStringInit(&safeUnloadName);
Tcl_DStringInit(&tmp);
- packageName = NULL;
+ prefix = NULL;
if (objc >= 3) {
- packageName = TclGetString(objv[2]);
- if (packageName[0] == '\0') {
- packageName = NULL;
+ prefix = TclGetString(objv[2]);
+ if (prefix[0] == '\0') {
+ prefix = NULL;
}
}
- if ((fullFileName[0] == 0) && (packageName == NULL)) {
+ if ((fullFileName[0] == 0) && (prefix == NULL)) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "must specify either file name or package name", -1));
+ "must specify either file name or prefix", -1));
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LOAD", "NOLIBRARY",
NULL);
code = TCL_ERROR;
@@ -191,7 +191,7 @@ Tcl_LoadObjCmd(
}
/*
- * Figure out which interpreter we're going to load the package into.
+ * Figure out which interpreter we're going to load the library into.
*/
target = interp;
@@ -206,89 +206,89 @@ Tcl_LoadObjCmd(
}
/*
- * Scan through the packages that are currently loaded to see if the
- * package we want is already loaded. We'll use a loaded package if it
+ * Scan through the libraries that are currently loaded to see if the
+ * library we want is already loaded. We'll use a loaded library if it
* meets any of the following conditions:
* - Its name and file match the once we're looking for.
* - Its file matches, and we weren't given a name.
* - Its name matches, the file name was specified as empty, and there is
- * only no statically loaded package with the same name.
+ * only no statically loaded library with the same prefix.
*/
- Tcl_MutexLock(&packageMutex);
+ Tcl_MutexLock(&libraryMutex);
defaultPtr = NULL;
- for (pkgPtr = firstPackagePtr; pkgPtr != NULL; pkgPtr = pkgPtr->nextPtr) {
- if (packageName == NULL) {
+ for (libraryPtr = firstLibraryPtr; libraryPtr != NULL; libraryPtr = libraryPtr->nextPtr) {
+ if (prefix == NULL) {
namesMatch = 0;
} else {
- TclDStringClear(&pkgName);
- Tcl_DStringAppend(&pkgName, packageName, -1);
+ TclDStringClear(&pfx);
+ Tcl_DStringAppend(&pfx, prefix, -1);
TclDStringClear(&tmp);
- Tcl_DStringAppend(&tmp, pkgPtr->packageName, -1);
- Tcl_UtfToLower(Tcl_DStringValue(&pkgName));
+ Tcl_DStringAppend(&tmp, libraryPtr->prefix, -1);
+ Tcl_UtfToLower(Tcl_DStringValue(&pfx));
Tcl_UtfToLower(Tcl_DStringValue(&tmp));
if (strcmp(Tcl_DStringValue(&tmp),
- Tcl_DStringValue(&pkgName)) == 0) {
+ Tcl_DStringValue(&pfx)) == 0) {
namesMatch = 1;
} else {
namesMatch = 0;
}
}
- TclDStringClear(&pkgName);
+ TclDStringClear(&pfx);
- filesMatch = (strcmp(pkgPtr->fileName, fullFileName) == 0);
- if (filesMatch && (namesMatch || (packageName == NULL))) {
+ filesMatch = (strcmp(libraryPtr->fileName, fullFileName) == 0);
+ if (filesMatch && (namesMatch || (prefix == NULL))) {
break;
}
if (namesMatch && (fullFileName[0] == 0)) {
- defaultPtr = pkgPtr;
+ defaultPtr = libraryPtr;
}
if (filesMatch && !namesMatch && (fullFileName[0] != 0)) {
/*
- * Can't have two different packages loaded from the same file.
+ * Can't have two different libraries loaded from the same file.
*/
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "file \"%s\" is already loaded for package \"%s\"",
- fullFileName, pkgPtr->packageName));
+ "file \"%s\" is already loaded for prefix \"%s\"",
+ fullFileName, libraryPtr->prefix));
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LOAD",
"SPLITPERSONALITY", NULL);
code = TCL_ERROR;
- Tcl_MutexUnlock(&packageMutex);
+ Tcl_MutexUnlock(&libraryMutex);
goto done;
}
}
- Tcl_MutexUnlock(&packageMutex);
- if (pkgPtr == NULL) {
- pkgPtr = defaultPtr;
+ Tcl_MutexUnlock(&libraryMutex);
+ if (libraryPtr == NULL) {
+ libraryPtr = defaultPtr;
}
/*
- * Scan through the list of packages already loaded in the target
- * interpreter. If the package we want is already loaded there, then
+ * Scan through the list of libraries already loaded in the target
+ * interpreter. If the library we want is already loaded there, then
* there's nothing for us to do.
*/
- if (pkgPtr != NULL) {
- ipFirstPtr = (InterpPackage *)Tcl_GetAssocData(target, "tclLoad", NULL);
+ if (libraryPtr != NULL) {
+ ipFirstPtr = (InterpLibrary *)Tcl_GetAssocData(target, "tclLoad", NULL);
for (ipPtr = ipFirstPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
- if (ipPtr->pkgPtr == pkgPtr) {
+ if (ipPtr->libraryPtr == libraryPtr) {
code = TCL_OK;
goto done;
}
}
}
- if (pkgPtr == NULL) {
+ if (libraryPtr == NULL) {
/*
* The desired file isn't currently loaded, so load it. It's an error
- * if the desired package is a static one.
+ * if the desired library is a static one.
*/
if (fullFileName[0] == 0) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "package \"%s\" isn't loaded statically", packageName));
+ "no library with prefix \"%s\" is loaded statically", prefix));
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LOAD", "NOTSTATIC",
NULL);
code = TCL_ERROR;
@@ -296,11 +296,11 @@ Tcl_LoadObjCmd(
}
/*
- * Figure out the module name if it wasn't provided explicitly.
+ * Figure out the prefix if it wasn't provided explicitly.
*/
- if (packageName != NULL) {
- Tcl_DStringAppend(&pkgName, packageName, -1);
+ if (prefix != NULL) {
+ Tcl_DStringAppend(&pfx, prefix, -1);
} else {
Tcl_Obj *splitPtr, *pkgGuessPtr;
int pElements;
@@ -311,11 +311,11 @@ Tcl_LoadObjCmd(
*/
/*
- * The platform-specific code couldn't figure out the module
- * name. Make a guess by taking the last element of the file
- * name, stripping off any leading "lib", and then using all
- * of the alphabetic and underline characters that follow
- * that.
+ * The platform-specific code couldn't figure out the prefix.
+ * Make a guess by taking the last element of the file
+ * name, stripping off any leading "lib" and/or "tcl", and
+ * then using all of the alphabetic and underline characters
+ * that follow that.
*/
splitPtr = Tcl_FSSplitPath(objv[1], &pElements);
@@ -346,85 +346,85 @@ Tcl_LoadObjCmd(
if (p == pkgGuess) {
Tcl_DecrRefCount(splitPtr);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "couldn't figure out package name for %s",
+ "couldn't figure out prefix for %s",
fullFileName));
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LOAD",
- "WHATPACKAGE", NULL);
+ "WHATLIBRARY", NULL);
code = TCL_ERROR;
goto done;
}
- Tcl_DStringAppend(&pkgName, pkgGuess, p - pkgGuess);
+ Tcl_DStringAppend(&pfx, pkgGuess, p - pkgGuess);
Tcl_DecrRefCount(splitPtr);
}
/*
- * Fix the capitalization in the package name so that the first
+ * Fix the capitalization in the prefix so that the first
* character is in caps (or title case) but the others are all
* lower-case.
*/
- Tcl_DStringSetLength(&pkgName,
- Tcl_UtfToTitle(Tcl_DStringValue(&pkgName)));
+ Tcl_DStringSetLength(&pfx,
+ Tcl_UtfToTitle(Tcl_DStringValue(&pfx)));
/*
* Compute the names of the two initialization functions, based on the
- * package name.
+ * prefix.
*/
- TclDStringAppendDString(&initName, &pkgName);
+ TclDStringAppendDString(&initName, &pfx);
TclDStringAppendLiteral(&initName, "_Init");
- TclDStringAppendDString(&safeInitName, &pkgName);
+ TclDStringAppendDString(&safeInitName, &pfx);
TclDStringAppendLiteral(&safeInitName, "_SafeInit");
- TclDStringAppendDString(&unloadName, &pkgName);
+ TclDStringAppendDString(&unloadName, &pfx);
TclDStringAppendLiteral(&unloadName, "_Unload");
- TclDStringAppendDString(&safeUnloadName, &pkgName);
+ TclDStringAppendDString(&safeUnloadName, &pfx);
TclDStringAppendLiteral(&safeUnloadName, "_SafeUnload");
/*
- * Call platform-specific code to load the package and find the two
+ * Call platform-specific code to load the library and find the two
* initialization functions.
*/
symbols[0] = Tcl_DStringValue(&initName);
symbols[1] = NULL;
- Tcl_MutexLock(&packageMutex);
+ Tcl_MutexLock(&libraryMutex);
code = Tcl_LoadFile(interp, objv[1], symbols, flags, &initProc,
&loadHandle);
- Tcl_MutexUnlock(&packageMutex);
+ Tcl_MutexUnlock(&libraryMutex);
if (code != TCL_OK) {
goto done;
}
/*
- * Create a new record to describe this package.
+ * Create a new record to describe this library.
*/
- pkgPtr = (LoadedPackage *)Tcl_Alloc(sizeof(LoadedPackage));
+ libraryPtr = (LoadedLibrary *)Tcl_Alloc(sizeof(LoadedLibrary));
len = strlen(fullFileName) + 1;
- pkgPtr->fileName = (char *)Tcl_Alloc(len);
- memcpy(pkgPtr->fileName, fullFileName, len);
- len = Tcl_DStringLength(&pkgName) + 1;
- pkgPtr->packageName = (char *)Tcl_Alloc(len);
- memcpy(pkgPtr->packageName, Tcl_DStringValue(&pkgName), len);
- pkgPtr->loadHandle = loadHandle;
- pkgPtr->initProc = initProc;
- pkgPtr->safeInitProc = (Tcl_PackageInitProc *)
+ libraryPtr->fileName = (char *)Tcl_Alloc(len);
+ memcpy(libraryPtr->fileName, fullFileName, len);
+ len = Tcl_DStringLength(&pfx) + 1;
+ libraryPtr->prefix = (char *)Tcl_Alloc(len);
+ memcpy(libraryPtr->prefix, Tcl_DStringValue(&pfx), len);
+ libraryPtr->loadHandle = loadHandle;
+ libraryPtr->initProc = initProc;
+ libraryPtr->safeInitProc = (Tcl_PackageInitProc *)
Tcl_FindSymbol(interp, loadHandle,
Tcl_DStringValue(&safeInitName));
- pkgPtr->unloadProc = (Tcl_PackageUnloadProc *)
+ libraryPtr->unloadProc = (Tcl_PackageUnloadProc *)
Tcl_FindSymbol(interp, loadHandle,
Tcl_DStringValue(&unloadName));
- pkgPtr->safeUnloadProc = (Tcl_PackageUnloadProc *)
+ libraryPtr->safeUnloadProc = (Tcl_PackageUnloadProc *)
Tcl_FindSymbol(interp, loadHandle,
Tcl_DStringValue(&safeUnloadName));
- pkgPtr->interpRefCount = 0;
- pkgPtr->safeInterpRefCount = 0;
+ libraryPtr->interpRefCount = 0;
+ libraryPtr->safeInterpRefCount = 0;
- Tcl_MutexLock(&packageMutex);
- pkgPtr->nextPtr = firstPackagePtr;
- firstPackagePtr = pkgPtr;
- Tcl_MutexUnlock(&packageMutex);
+ Tcl_MutexLock(&libraryMutex);
+ libraryPtr->nextPtr = firstLibraryPtr;
+ firstLibraryPtr = libraryPtr;
+ Tcl_MutexUnlock(&libraryMutex);
/*
* The Tcl_FindSymbol calls may have left a spurious error message in
@@ -435,32 +435,32 @@ Tcl_LoadObjCmd(
}
/*
- * Invoke the package's initialization function (either the normal one or
+ * Invoke the library's initialization function (either the normal one or
* the safe one, depending on whether or not the interpreter is safe).
*/
if (Tcl_IsSafe(target)) {
- if (pkgPtr->safeInitProc == NULL) {
+ if (libraryPtr->safeInitProc == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "can't use package in a safe interpreter: no"
- " %s_SafeInit procedure", pkgPtr->packageName));
+ "can't use library in a safe interpreter: no"
+ " %s_SafeInit procedure", libraryPtr->prefix));
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LOAD", "UNSAFE",
NULL);
code = TCL_ERROR;
goto done;
}
- code = pkgPtr->safeInitProc(target);
+ code = libraryPtr->safeInitProc(target);
} else {
- if (pkgPtr->initProc == NULL) {
+ if (libraryPtr->initProc == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "can't attach package to interpreter: no %s_Init procedure",
- pkgPtr->packageName));
+ "can't attach library to interpreter: no %s_Init procedure",
+ libraryPtr->prefix));
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LOAD", "ENTRYPOINT",
NULL);
code = TCL_ERROR;
goto done;
}
- code = pkgPtr->initProc(target);
+ code = libraryPtr->initProc(target);
}
/*
@@ -485,33 +485,33 @@ Tcl_LoadObjCmd(
}
/*
- * Record the fact that the package has been loaded in the target
+ * Record the fact that the library has been loaded in the target
* interpreter.
*
* Update the proper reference count.
*/
- Tcl_MutexLock(&packageMutex);
+ Tcl_MutexLock(&libraryMutex);
if (Tcl_IsSafe(target)) {
- pkgPtr->safeInterpRefCount++;
+ libraryPtr->safeInterpRefCount++;
} else {
- pkgPtr->interpRefCount++;
+ libraryPtr->interpRefCount++;
}
- Tcl_MutexUnlock(&packageMutex);
+ Tcl_MutexUnlock(&libraryMutex);
/*
- * Refetch ipFirstPtr: loading the package may have introduced additional
- * static packages at the head of the linked list!
+ * Refetch ipFirstPtr: loading the library may have introduced additional
+ * static libraries at the head of the linked list!
*/
- ipFirstPtr = (InterpPackage *)Tcl_GetAssocData(target, "tclLoad", NULL);
- ipPtr = (InterpPackage *)Tcl_Alloc(sizeof(InterpPackage));
- ipPtr->pkgPtr = pkgPtr;
+ ipFirstPtr = (InterpLibrary *)Tcl_GetAssocData(target, "tclLoad", NULL);
+ ipPtr = (InterpLibrary *)Tcl_Alloc(sizeof(InterpLibrary));
+ ipPtr->libraryPtr = libraryPtr;
ipPtr->nextPtr = ipFirstPtr;
Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc, ipPtr);
done:
- Tcl_DStringFree(&pkgName);
+ Tcl_DStringFree(&pfx);
Tcl_DStringFree(&initName);
Tcl_DStringFree(&safeInitName);
Tcl_DStringFree(&unloadName);
@@ -545,14 +545,14 @@ Tcl_UnloadObjCmd(
Tcl_Obj *const objv[]) /* Argument objects. */
{
Tcl_Interp *target; /* Which interpreter to unload from. */
- LoadedPackage *pkgPtr, *defaultPtr;
- Tcl_DString pkgName, tmp;
+ LoadedLibrary *libraryPtr, *defaultPtr;
+ Tcl_DString pfx, tmp;
Tcl_PackageUnloadProc *unloadProc;
- InterpPackage *ipFirstPtr, *ipPtr;
+ InterpLibrary *ipFirstPtr, *ipPtr;
int i, index, code, complain = 1, keepLibrary = 0;
int trustedRefCount = -1, safeRefCount = -1;
const char *fullFileName = "";
- const char *packageName;
+ const char *prefix;
static const char *const options[] = {
"-nocomplain", "-keeplibrary", "--", NULL
};
@@ -596,7 +596,7 @@ Tcl_UnloadObjCmd(
endOfForLoop:
if ((objc-i < 1) || (objc-i > 3)) {
Tcl_WrongNumArgs(interp, 1, objv,
- "?-switch ...? fileName ?packageName? ?interp?");
+ "?-switch ...? fileName ?prefix? ?interp?");
return TCL_ERROR;
}
if (Tcl_FSConvertToPathType(interp, objv[i]) != TCL_OK) {
@@ -604,19 +604,19 @@ Tcl_UnloadObjCmd(
}
fullFileName = TclGetString(objv[i]);
- Tcl_DStringInit(&pkgName);
+ Tcl_DStringInit(&pfx);
Tcl_DStringInit(&tmp);
- packageName = NULL;
+ prefix = NULL;
if (objc - i >= 2) {
- packageName = TclGetString(objv[i+1]);
- if (packageName[0] == '\0') {
- packageName = NULL;
+ prefix = TclGetString(objv[i+1]);
+ if (prefix[0] == '\0') {
+ prefix = NULL;
}
}
- if ((fullFileName[0] == 0) && (packageName == NULL)) {
+ if ((fullFileName[0] == 0) && (prefix == NULL)) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "must specify either file name or package name", -1));
+ "must specify either file name or prefix", -1));
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "UNLOAD", "NOLIBRARY",
NULL);
code = TCL_ERROR;
@@ -624,7 +624,7 @@ Tcl_UnloadObjCmd(
}
/*
- * Figure out which interpreter we're going to load the package into.
+ * Figure out which interpreter we're going to load the library into.
*/
target = interp;
@@ -638,65 +638,65 @@ Tcl_UnloadObjCmd(
}
/*
- * Scan through the packages that are currently loaded to see if the
- * package we want is already loaded. We'll use a loaded package if it
+ * Scan through the libraries that are currently loaded to see if the
+ * library we want is already loaded. We'll use a loaded library if it
* meets any of the following conditions:
- * - Its name and file match the once we're looking for.
- * - Its file matches, and we weren't given a name.
- * - Its name matches, the file name was specified as empty, and there is
- * only no statically loaded package with the same name.
+ * - Its prefix and file match the once we're looking for.
+ * - Its file matches, and we weren't given a prefix.
+ * - Its prefix matches, the file name was specified as empty, and there is
+ * only no statically loaded library with the same prefix.
*/
- Tcl_MutexLock(&packageMutex);
+ Tcl_MutexLock(&libraryMutex);
defaultPtr = NULL;
- for (pkgPtr = firstPackagePtr; pkgPtr != NULL; pkgPtr = pkgPtr->nextPtr) {
+ for (libraryPtr = firstLibraryPtr; libraryPtr != NULL; libraryPtr = libraryPtr->nextPtr) {
int namesMatch, filesMatch;
- if (packageName == NULL) {
+ if (prefix == NULL) {
namesMatch = 0;
} else {
- TclDStringClear(&pkgName);
- Tcl_DStringAppend(&pkgName, packageName, -1);
+ TclDStringClear(&pfx);
+ Tcl_DStringAppend(&pfx, prefix, -1);
TclDStringClear(&tmp);
- Tcl_DStringAppend(&tmp, pkgPtr->packageName, -1);
- Tcl_UtfToLower(Tcl_DStringValue(&pkgName));
+ Tcl_DStringAppend(&tmp, libraryPtr->prefix, -1);
+ Tcl_UtfToLower(Tcl_DStringValue(&pfx));
Tcl_UtfToLower(Tcl_DStringValue(&tmp));
if (strcmp(Tcl_DStringValue(&tmp),
- Tcl_DStringValue(&pkgName)) == 0) {
+ Tcl_DStringValue(&pfx)) == 0) {
namesMatch = 1;
} else {
namesMatch = 0;
}
}
- TclDStringClear(&pkgName);
+ TclDStringClear(&pfx);
- filesMatch = (strcmp(pkgPtr->fileName, fullFileName) == 0);
- if (filesMatch && (namesMatch || (packageName == NULL))) {
+ filesMatch = (strcmp(libraryPtr->fileName, fullFileName) == 0);
+ if (filesMatch && (namesMatch || (prefix == NULL))) {
break;
}
if (namesMatch && (fullFileName[0] == 0)) {
- defaultPtr = pkgPtr;
+ defaultPtr = libraryPtr;
}
if (filesMatch && !namesMatch && (fullFileName[0] != 0)) {
break;
}
}
- Tcl_MutexUnlock(&packageMutex);
+ Tcl_MutexUnlock(&libraryMutex);
if (fullFileName[0] == 0) {
/*
- * It's an error to try unload a static package.
+ * It's an error to try unload a static library.
*/
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "package \"%s\" is loaded statically and cannot be unloaded",
- packageName));
+ "library with prefix \"%s\" is loaded statically and cannot be unloaded",
+ prefix));
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "UNLOAD", "STATIC",
NULL);
code = TCL_ERROR;
goto done;
}
- if (pkgPtr == NULL) {
+ if (libraryPtr == NULL) {
/*
* The DLL pointed by the provided filename has never been loaded.
*/
@@ -710,16 +710,16 @@ Tcl_UnloadObjCmd(
}
/*
- * Scan through the list of packages already loaded in the target
- * interpreter. If the package we want is already loaded there, then we
+ * Scan through the list of libraries already loaded in the target
+ * interpreter. If the library we want is already loaded there, then we
* should proceed with unloading.
*/
code = TCL_ERROR;
- if (pkgPtr != NULL) {
- ipFirstPtr = (InterpPackage *)Tcl_GetAssocData(target, "tclLoad", NULL);
+ if (libraryPtr != NULL) {
+ ipFirstPtr = (InterpLibrary *)Tcl_GetAssocData(target, "tclLoad", NULL);
for (ipPtr = ipFirstPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
- if (ipPtr->pkgPtr == pkgPtr) {
+ if (ipPtr->libraryPtr == libraryPtr) {
code = TCL_OK;
break;
}
@@ -727,7 +727,7 @@ Tcl_UnloadObjCmd(
}
if (code != TCL_OK) {
/*
- * The package has not been loaded in this interpreter.
+ * The library has not been loaded in this interpreter.
*/
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -741,12 +741,12 @@ Tcl_UnloadObjCmd(
/*
* Ensure that the DLL can be unloaded. If it is a trusted interpreter,
- * pkgPtr->unloadProc must not be NULL for the DLL to be unloadable. If
- * the interpreter is a safe one, pkgPtr->safeUnloadProc must be non-NULL.
+ * libraryPtr->unloadProc must not be NULL for the DLL to be unloadable. If
+ * the interpreter is a safe one, libraryPtr->safeUnloadProc must be non-NULL.
*/
if (Tcl_IsSafe(target)) {
- if (pkgPtr->safeUnloadProc == NULL) {
+ if (libraryPtr->safeUnloadProc == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"file \"%s\" cannot be unloaded under a safe interpreter",
fullFileName));
@@ -755,9 +755,9 @@ Tcl_UnloadObjCmd(
code = TCL_ERROR;
goto done;
}
- unloadProc = pkgPtr->safeUnloadProc;
+ unloadProc = libraryPtr->safeUnloadProc;
} else {
- if (pkgPtr->unloadProc == NULL) {
+ if (libraryPtr->unloadProc == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"file \"%s\" cannot be unloaded under a trusted interpreter",
fullFileName));
@@ -766,11 +766,11 @@ Tcl_UnloadObjCmd(
code = TCL_ERROR;
goto done;
}
- unloadProc = pkgPtr->unloadProc;
+ unloadProc = libraryPtr->unloadProc;
}
/*
- * We are ready to unload the package. First, evaluate the unload
+ * We are ready to unload the library. First, evaluate the unload
* function. If this fails, we cannot proceed with unload. Also, we must
* specify the proper flag to pass to the unload callback.
* TCL_UNLOAD_DETACH_FROM_INTERPRETER is defined when the callback should
@@ -781,10 +781,10 @@ Tcl_UnloadObjCmd(
code = TCL_UNLOAD_DETACH_FROM_INTERPRETER;
if (!keepLibrary) {
- Tcl_MutexLock(&packageMutex);
- trustedRefCount = pkgPtr->interpRefCount;
- safeRefCount = pkgPtr->safeInterpRefCount;
- Tcl_MutexUnlock(&packageMutex);
+ Tcl_MutexLock(&libraryMutex);
+ trustedRefCount = libraryPtr->interpRefCount;
+ safeRefCount = libraryPtr->safeInterpRefCount;
+ Tcl_MutexUnlock(&libraryMutex);
if (Tcl_IsSafe(target)) {
safeRefCount--;
@@ -807,34 +807,34 @@ Tcl_UnloadObjCmd(
* if we unload the DLL.
*/
- Tcl_MutexLock(&packageMutex);
+ Tcl_MutexLock(&libraryMutex);
if (Tcl_IsSafe(target)) {
- pkgPtr->safeInterpRefCount--;
+ libraryPtr->safeInterpRefCount--;
/*
* Do not let counter get negative.
*/
- if (pkgPtr->safeInterpRefCount < 0) {
- pkgPtr->safeInterpRefCount = 0;
+ if (libraryPtr->safeInterpRefCount < 0) {
+ libraryPtr->safeInterpRefCount = 0;
}
} else {
- pkgPtr->interpRefCount--;
+ libraryPtr->interpRefCount--;
/*
* Do not let counter get negative.
*/
- if (pkgPtr->interpRefCount < 0) {
- pkgPtr->interpRefCount = 0;
+ if (libraryPtr->interpRefCount < 0) {
+ libraryPtr->interpRefCount = 0;
}
}
- trustedRefCount = pkgPtr->interpRefCount;
- safeRefCount = pkgPtr->safeInterpRefCount;
- Tcl_MutexUnlock(&packageMutex);
+ trustedRefCount = libraryPtr->interpRefCount;
+ safeRefCount = libraryPtr->safeInterpRefCount;
+ Tcl_MutexUnlock(&libraryMutex);
code = TCL_OK;
- if (pkgPtr->safeInterpRefCount <= 0 && pkgPtr->interpRefCount <= 0
+ if (libraryPtr->safeInterpRefCount <= 0 && libraryPtr->interpRefCount <= 0
&& !keepLibrary) {
/*
* Unload the shared library from the application memory...
@@ -848,21 +848,21 @@ Tcl_UnloadObjCmd(
* it's been unloaded.
*/
- if (pkgPtr->fileName[0] != '\0') {
- Tcl_MutexLock(&packageMutex);
- if (Tcl_FSUnloadFile(interp, pkgPtr->loadHandle) == TCL_OK) {
+ if (libraryPtr->fileName[0] != '\0') {
+ Tcl_MutexLock(&libraryMutex);
+ if (Tcl_FSUnloadFile(interp, libraryPtr->loadHandle) == TCL_OK) {
/*
* Remove this library from the loaded library cache.
*/
- defaultPtr = pkgPtr;
- if (defaultPtr == firstPackagePtr) {
- firstPackagePtr = pkgPtr->nextPtr;
+ defaultPtr = libraryPtr;
+ if (defaultPtr == firstLibraryPtr) {
+ firstLibraryPtr = libraryPtr->nextPtr;
} else {
- for (pkgPtr = firstPackagePtr; pkgPtr != NULL;
- pkgPtr = pkgPtr->nextPtr) {
- if (pkgPtr->nextPtr == defaultPtr) {
- pkgPtr->nextPtr = defaultPtr->nextPtr;
+ for (libraryPtr = firstLibraryPtr; libraryPtr != NULL;
+ libraryPtr = libraryPtr->nextPtr) {
+ if (libraryPtr->nextPtr == defaultPtr) {
+ libraryPtr->nextPtr = defaultPtr->nextPtr;
break;
}
}
@@ -872,16 +872,16 @@ Tcl_UnloadObjCmd(
* Remove this library from the interpreter's library cache.
*/
- ipFirstPtr = (InterpPackage *)Tcl_GetAssocData(target, "tclLoad", NULL);
+ ipFirstPtr = (InterpLibrary *)Tcl_GetAssocData(target, "tclLoad", NULL);
ipPtr = ipFirstPtr;
- if (ipPtr->pkgPtr == defaultPtr) {
+ if (ipPtr->libraryPtr == defaultPtr) {
ipFirstPtr = ipFirstPtr->nextPtr;
} else {
- InterpPackage *ipPrevPtr;
+ InterpLibrary *ipPrevPtr;
for (ipPrevPtr = ipPtr; ipPtr != NULL;
ipPrevPtr = ipPtr, ipPtr = ipPtr->nextPtr) {
- if (ipPtr->pkgPtr == defaultPtr) {
+ if (ipPtr->libraryPtr == defaultPtr) {
ipPrevPtr->nextPtr = ipPtr->nextPtr;
break;
}
@@ -890,10 +890,10 @@ Tcl_UnloadObjCmd(
Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc,
ipFirstPtr);
Tcl_Free(defaultPtr->fileName);
- Tcl_Free(defaultPtr->packageName);
+ Tcl_Free(defaultPtr->prefix);
Tcl_Free(defaultPtr);
Tcl_Free(ipPtr);
- Tcl_MutexUnlock(&packageMutex);
+ Tcl_MutexUnlock(&libraryMutex);
} else {
code = TCL_ERROR;
}
@@ -909,7 +909,7 @@ Tcl_UnloadObjCmd(
}
done:
- Tcl_DStringFree(&pkgName);
+ Tcl_DStringFree(&pfx);
Tcl_DStringFree(&tmp);
if (!complain && (code != TCL_OK)) {
code = TCL_OK;
@@ -923,14 +923,14 @@ Tcl_UnloadObjCmd(
*
* Tcl_StaticPackage --
*
- * This function is invoked to indicate that a particular package has
+ * This function is invoked to indicate that a particular library has
* been linked statically with an application.
*
* Results:
* None.
*
* Side effects:
- * Once this function completes, the package becomes loadable via the
+ * Once this function completes, the library becomes loadable via the
* "load" command with an empty file name.
*
*----------------------------------------------------------------------
@@ -938,82 +938,82 @@ Tcl_UnloadObjCmd(
void
Tcl_StaticPackage(
- Tcl_Interp *interp, /* If not NULL, it means that the package has
+ Tcl_Interp *interp, /* If not NULL, it means that the library has
* already been loaded into the given
* interpreter by calling the appropriate init
* proc. */
- const char *pkgName, /* Name of package (must be properly
+ const char *prefix, /* Prefix (must be properly
* capitalized: first letter upper case,
* others lower case). */
Tcl_PackageInitProc *initProc,
/* Function to call to incorporate this
- * package into a trusted interpreter. */
+ * library into a trusted interpreter. */
Tcl_PackageInitProc *safeInitProc)
/* Function to call to incorporate this
- * package into a safe interpreter (one that
+ * library into a safe interpreter (one that
* will execute untrusted scripts). NULL means
- * the package can't be used in safe
+ * the library can't be used in safe
* interpreters. */
{
- LoadedPackage *pkgPtr;
- InterpPackage *ipPtr, *ipFirstPtr;
+ LoadedLibrary *libraryPtr;
+ InterpLibrary *ipPtr, *ipFirstPtr;
/*
- * Check to see if someone else has already reported this package as
+ * Check to see if someone else has already reported this library as
* statically loaded in the process.
*/
- Tcl_MutexLock(&packageMutex);
- for (pkgPtr = firstPackagePtr; pkgPtr != NULL; pkgPtr = pkgPtr->nextPtr) {
- if ((pkgPtr->initProc == initProc)
- && (pkgPtr->safeInitProc == safeInitProc)
- && (strcmp(pkgPtr->packageName, pkgName) == 0)) {
+ Tcl_MutexLock(&libraryMutex);
+ for (libraryPtr = firstLibraryPtr; libraryPtr != NULL; libraryPtr = libraryPtr->nextPtr) {
+ if ((libraryPtr->initProc == initProc)
+ && (libraryPtr->safeInitProc == safeInitProc)
+ && (strcmp(libraryPtr->prefix, prefix) == 0)) {
break;
}
}
- Tcl_MutexUnlock(&packageMutex);
+ Tcl_MutexUnlock(&libraryMutex);
/*
- * If the package is not yet recorded as being loaded statically, add it
+ * If the library is not yet recorded as being loaded statically, add it
* to the list now.
*/
- if (pkgPtr == NULL) {
- pkgPtr = (LoadedPackage *)Tcl_Alloc(sizeof(LoadedPackage));
- pkgPtr->fileName = (char *)Tcl_Alloc(1);
- pkgPtr->fileName[0] = 0;
- pkgPtr->packageName = (char *)Tcl_Alloc(strlen(pkgName) + 1);
- strcpy(pkgPtr->packageName, pkgName);
- pkgPtr->loadHandle = NULL;
- pkgPtr->initProc = initProc;
- pkgPtr->safeInitProc = safeInitProc;
- Tcl_MutexLock(&packageMutex);
- pkgPtr->nextPtr = firstPackagePtr;
- firstPackagePtr = pkgPtr;
- Tcl_MutexUnlock(&packageMutex);
+ if (libraryPtr == NULL) {
+ libraryPtr = (LoadedLibrary *)Tcl_Alloc(sizeof(LoadedLibrary));
+ libraryPtr->fileName = (char *)Tcl_Alloc(1);
+ libraryPtr->fileName[0] = 0;
+ libraryPtr->prefix = (char *)Tcl_Alloc(strlen(prefix) + 1);
+ strcpy(libraryPtr->prefix, prefix);
+ libraryPtr->loadHandle = NULL;
+ libraryPtr->initProc = initProc;
+ libraryPtr->safeInitProc = safeInitProc;
+ Tcl_MutexLock(&libraryMutex);
+ libraryPtr->nextPtr = firstLibraryPtr;
+ firstLibraryPtr = libraryPtr;
+ Tcl_MutexUnlock(&libraryMutex);
}
if (interp != NULL) {
/*
- * If we're loading the package into an interpreter, determine whether
+ * If we're loading the library into an interpreter, determine whether
* it's already loaded.
*/
- ipFirstPtr = (InterpPackage *)Tcl_GetAssocData(interp, "tclLoad", NULL);
+ ipFirstPtr = (InterpLibrary *)Tcl_GetAssocData(interp, "tclLoad", NULL);
for (ipPtr = ipFirstPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
- if (ipPtr->pkgPtr == pkgPtr) {
+ if (ipPtr->libraryPtr == libraryPtr) {
return;
}
}
/*
- * Package isn't loaded in the current interp yet. Mark it as now being
+ * Library isn't loaded in the current interp yet. Mark it as now being
* loaded.
*/
- ipPtr = (InterpPackage *)Tcl_Alloc(sizeof(InterpPackage));
- ipPtr->pkgPtr = pkgPtr;
+ ipPtr = (InterpLibrary *)Tcl_Alloc(sizeof(InterpLibrary));
+ ipPtr->libraryPtr = libraryPtr;
ipPtr->nextPtr = ipFirstPtr;
Tcl_SetAssocData(interp, "tclLoad", LoadCleanupProc, ipPtr);
}
@@ -1022,7 +1022,7 @@ Tcl_StaticPackage(
/*
*----------------------------------------------------------------------
*
- * TclGetLoadedPackagesEx --
+ * TclGetLoadedLibraries --
*
* This function returns information about all of the files that are
* loaded (either in a particular interpreter, or for all interpreters).
@@ -1032,7 +1032,7 @@ Tcl_StaticPackage(
* list of lists is placed in the interp's result. Each sublist
* corresponds to one loaded file; its first element is the name of the
* file (or an empty string for something that's statically loaded) and
- * the second element is the name of the package in that file.
+ * the second element is the prefix of the library in that file.
*
* Side effects:
* None.
@@ -1041,33 +1041,33 @@ Tcl_StaticPackage(
*/
int
-TclGetLoadedPackagesEx(
+TclGetLoadedLibraries(
Tcl_Interp *interp, /* Interpreter in which to return information
* or error message. */
const char *targetName, /* Name of target interpreter or NULL. If
* NULL, return info about all interps;
* otherwise, just return info about this
* interpreter. */
- const char *packageName) /* Package name or NULL. If NULL, return info
- * for all packages.
+ const char *prefix) /* Prefix or NULL. If NULL, return info
+ * for all prefixes.
*/
{
Tcl_Interp *target;
- LoadedPackage *pkgPtr;
- InterpPackage *ipPtr;
+ LoadedLibrary *libraryPtr;
+ InterpLibrary *ipPtr;
Tcl_Obj *resultObj, *pkgDesc[2];
if (targetName == NULL) {
TclNewObj(resultObj);
- Tcl_MutexLock(&packageMutex);
- for (pkgPtr = firstPackagePtr; pkgPtr != NULL;
- pkgPtr = pkgPtr->nextPtr) {
- pkgDesc[0] = Tcl_NewStringObj(pkgPtr->fileName, -1);
- pkgDesc[1] = Tcl_NewStringObj(pkgPtr->packageName, -1);
+ Tcl_MutexLock(&libraryMutex);
+ for (libraryPtr = firstLibraryPtr; libraryPtr != NULL;
+ libraryPtr = libraryPtr->nextPtr) {
+ pkgDesc[0] = Tcl_NewStringObj(libraryPtr->fileName, -1);
+ pkgDesc[1] = Tcl_NewStringObj(libraryPtr->prefix, -1);
Tcl_ListObjAppendElement(NULL, resultObj,
Tcl_NewListObj(2, pkgDesc));
}
- Tcl_MutexUnlock(&packageMutex);
+ Tcl_MutexUnlock(&libraryMutex);
Tcl_SetObjResult(interp, resultObj);
return TCL_OK;
}
@@ -1076,19 +1076,19 @@ TclGetLoadedPackagesEx(
if (target == NULL) {
return TCL_ERROR;
}
- ipPtr = (InterpPackage *)Tcl_GetAssocData(target, "tclLoad", NULL);
+ ipPtr = (InterpLibrary *)Tcl_GetAssocData(target, "tclLoad", NULL);
/*
- * Return information about all of the available packages.
+ * Return information about all of the available libraries.
*/
- if (packageName) {
+ if (prefix) {
resultObj = NULL;
for (; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
- pkgPtr = ipPtr->pkgPtr;
+ libraryPtr = ipPtr->libraryPtr;
- if (!strcmp(packageName, pkgPtr->packageName)) {
- resultObj = Tcl_NewStringObj(pkgPtr->fileName, -1);
+ if (!strcmp(prefix, libraryPtr->prefix)) {
+ resultObj = Tcl_NewStringObj(libraryPtr->fileName, -1);
break;
}
}
@@ -1100,15 +1100,15 @@ TclGetLoadedPackagesEx(
}
/*
- * Return information about only the packages that are loaded in a given
+ * Return information about only the libraries that are loaded in a given
* interpreter.
*/
TclNewObj(resultObj);
for (; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
- pkgPtr = ipPtr->pkgPtr;
- pkgDesc[0] = Tcl_NewStringObj(pkgPtr->fileName, -1);
- pkgDesc[1] = Tcl_NewStringObj(pkgPtr->packageName, -1);
+ libraryPtr = ipPtr->libraryPtr;
+ pkgDesc[0] = Tcl_NewStringObj(libraryPtr->fileName, -1);
+ pkgDesc[1] = Tcl_NewStringObj(libraryPtr->prefix, -1);
Tcl_ListObjAppendElement(NULL, resultObj, Tcl_NewListObj(2, pkgDesc));
}
Tcl_SetObjResult(interp, resultObj);
@@ -1120,7 +1120,7 @@ TclGetLoadedPackagesEx(
*
* LoadCleanupProc --
*
- * This function is called to delete all of the InterpPackage structures
+ * This function is called to delete all of the InterpLibrary structures
* for an interpreter when the interpreter is deleted. It gets invoked
* via the Tcl AssocData mechanism.
*
@@ -1128,20 +1128,20 @@ TclGetLoadedPackagesEx(
* None.
*
* Side effects:
- * Storage for all of the InterpPackage functions for interp get deleted.
+ * Storage for all of the InterpLibrary functions for interp get deleted.
*
*----------------------------------------------------------------------
*/
static void
LoadCleanupProc(
- ClientData clientData, /* Pointer to first InterpPackage structure
+ ClientData clientData, /* Pointer to first InterpLibrary structure
* for interp. */
TCL_UNUSED(Tcl_Interp *))
{
- InterpPackage *ipPtr, *nextPtr;
+ InterpLibrary *ipPtr, *nextPtr;
- ipPtr = (InterpPackage *)clientData;
+ ipPtr = (InterpLibrary *)clientData;
while (ipPtr != NULL) {
nextPtr = ipPtr->nextPtr;
Tcl_Free(ipPtr);
@@ -1155,7 +1155,7 @@ LoadCleanupProc(
* TclFinalizeLoad --
*
* This function is invoked just before the application exits. It frees
- * all of the LoadedPackage structures.
+ * all of the LoadedLibrary structures.
*
* Results:
* None.
@@ -1169,18 +1169,18 @@ LoadCleanupProc(
void
TclFinalizeLoad(void)
{
- LoadedPackage *pkgPtr;
+ LoadedLibrary *libraryPtr;
/*
* No synchronization here because there should just be one thread alive
- * at this point. Logically, packageMutex should be grabbed at this point,
+ * at this point. Logically, libraryMutex should be grabbed at this point,
* but the Mutexes get finalized before the call to this routine. The only
* subsystem left alive at this point is the memory allocator.
*/
- while (firstPackagePtr != NULL) {
- pkgPtr = firstPackagePtr;
- firstPackagePtr = pkgPtr->nextPtr;
+ while (firstLibraryPtr != NULL) {
+ libraryPtr = firstLibraryPtr;
+ firstLibraryPtr = libraryPtr->nextPtr;
#if defined(TCL_UNLOAD_DLLS) || defined(_WIN32)
/*
@@ -1190,14 +1190,14 @@ TclFinalizeLoad(void)
* it has been unloaded.
*/
- if (pkgPtr->fileName[0] != '\0') {
- Tcl_FSUnloadFile(NULL, pkgPtr->loadHandle);
+ if (libraryPtr->fileName[0] != '\0') {
+ Tcl_FSUnloadFile(NULL, libraryPtr->loadHandle);
}
#endif
- Tcl_Free(pkgPtr->fileName);
- Tcl_Free(pkgPtr->packageName);
- Tcl_Free(pkgPtr);
+ Tcl_Free(libraryPtr->fileName);
+ Tcl_Free(libraryPtr->prefix);
+ Tcl_Free(libraryPtr);
}
}
diff --git a/generic/tclParse.c b/generic/tclParse.c
index f894906..614401f 100644
--- a/generic/tclParse.c
+++ b/generic/tclParse.c
@@ -936,7 +936,7 @@ TclParseBackslash(
* #217987] test subst-3.2
*/
- if (TclUCS4Complete(p, numBytes - 1)) {
+ if (Tcl_UtfCharComplete(p, numBytes - 1)) {
count = TclUtfToUCS4(p, &unichar) + 1; /* +1 for '\' */
} else {
char utfBytes[8];
diff --git a/generic/tclPkgConfig.c b/generic/tclPkgConfig.c
index 672d3c9..d84472c 100644
--- a/generic/tclPkgConfig.c
+++ b/generic/tclPkgConfig.c
@@ -2,7 +2,7 @@
* tclPkgConfig.c --
*
* This file contains the configuration information to embed into the tcl
- * binary library.
+ * library.
*
* Copyright © 2002 Andreas Kupries <andreas_kupries@users.sourceforge.net>
*
diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c
index 273d1f7..78eafa7 100644
--- a/generic/tclStringObj.c
+++ b/generic/tclStringObj.c
@@ -1127,10 +1127,10 @@ Tcl_AppendLimitedToObj(
}
eLen = strlen(ellipsis);
while (eLen > limit) {
- eLen = TclUtfPrev(ellipsis+eLen, ellipsis) - ellipsis;
+ eLen = Tcl_UtfPrev(ellipsis+eLen, ellipsis) - ellipsis;
}
- toCopy = TclUtfPrev(bytes+limit+1-eLen, bytes) - bytes;
+ toCopy = Tcl_UtfPrev(bytes+limit+1-eLen, bytes) - bytes;
}
/*
@@ -2537,7 +2537,7 @@ AppendPrintfToObjVA(
* multi-byte characters.
*/
- q = TclUtfPrev(end, bytes);
+ q = Tcl_UtfPrev(end, bytes);
if (!Tcl_UtfCharComplete(q, (end - q))) {
end = q;
}
@@ -3348,7 +3348,7 @@ TclStringCmp(
s1 = (char *) Tcl_GetUnicode(value1Ptr);
s2 = (char *) Tcl_GetUnicode(value2Ptr);
if (
-#ifdef WORDS_BIGENDIAN
+#if defined(WORDS_BIGENDIAN) && (TCL_UTF_MAX > 3)
1
#else
checkEq
diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c
index 2248238..c9169bb 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -66,6 +66,7 @@
#undef Tcl_UniCharToUtfDString
#undef Tcl_UtfToUniCharDString
#undef Tcl_UtfToUniChar
+#define TclUnusedStubEntry 0
#if TCL_UTF_MAX <= 3
static void uniCodePanic() {
@@ -77,6 +78,11 @@ static void uniCodePanic() {
# define Tcl_SetUnicodeObj (void(*)(Tcl_Obj *, const Tcl_UniChar *, size_t))(void *)uniCodePanic
#endif
+#define TclUtfCharComplete Tcl_UtfCharComplete
+#define TclUtfNext Tcl_UtfNext
+#define TclUtfPrev Tcl_UtfPrev
+
+
#define TclBN_mp_add mp_add
#define TclBN_mp_add_d mp_add_d
#define TclBN_mp_and mp_and
@@ -526,7 +532,7 @@ static const TclIntStubs tclIntStubs = {
TclGetSrcInfoForPc, /* 233 */
TclVarHashCreateVar, /* 234 */
TclInitVarHashTable, /* 235 */
- 0, /* 236 */
+ TclAppendUnicodeToObj, /* 236 */
TclResetCancellation, /* 237 */
TclNRInterpProc, /* 238 */
TclNRInterpProcCore, /* 239 */
@@ -549,8 +555,7 @@ static const TclIntStubs tclIntStubs = {
TclPtrUnsetVar, /* 256 */
TclStaticPackage, /* 257 */
TclpCreateTemporaryDirectory, /* 258 */
- TclAppendUnicodeToObj, /* 259 */
- TclGetBytesFromObj, /* 260 */
+ TclGetBytesFromObj, /* 259 */
};
static const TclIntPlatStubs tclIntPlatStubs = {
@@ -1086,12 +1091,12 @@ const TclStubs tclStubs = {
Tcl_UniCharToUpper, /* 323 */
Tcl_UniCharToUtf, /* 324 */
Tcl_UtfAtIndex, /* 325 */
- Tcl_UtfCharComplete, /* 326 */
+ TclUtfCharComplete, /* 326 */
Tcl_UtfBackslash, /* 327 */
Tcl_UtfFindFirst, /* 328 */
Tcl_UtfFindLast, /* 329 */
- Tcl_UtfNext, /* 330 */
- Tcl_UtfPrev, /* 331 */
+ TclUtfNext, /* 330 */
+ TclUtfPrev, /* 331 */
Tcl_UtfToExternal, /* 332 */
Tcl_UtfToExternalDString, /* 333 */
Tcl_UtfToLower, /* 334 */
@@ -1414,6 +1419,9 @@ const TclStubs tclStubs = {
Tcl_GetStringFromObj, /* 651 */
Tcl_GetUnicodeFromObj, /* 652 */
Tcl_GetByteArrayFromObj, /* 653 */
+ Tcl_UtfCharComplete, /* 654 */
+ Tcl_UtfNext, /* 655 */
+ Tcl_UtfPrev, /* 656 */
};
/* !END!: Do not edit above this line. */
diff --git a/generic/tclTest.c b/generic/tclTest.c
index df61dbb..a51f473 100644
--- a/generic/tclTest.c
+++ b/generic/tclTest.c
@@ -4243,7 +4243,7 @@ TeststaticpkgCmd(
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # arguments: should be \"",
- argv[0], " pkgName safe loaded\"", NULL);
+ argv[0], " prefix safe loaded\"", NULL);
return TCL_ERROR;
}
if (Tcl_GetInt(interp, argv[2], &safe) != TCL_OK) {
@@ -6963,7 +6963,7 @@ TestUtfPrevCmd(
} else {
offset = numBytes;
}
- result = TclUtfPrev(bytes + offset, bytes);
+ result = Tcl_UtfPrev(bytes + offset, bytes);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(result - bytes));
return TCL_OK;
}
diff --git a/generic/tclThreadAlloc.c b/generic/tclThreadAlloc.c
index f343196..1eb6315 100644
--- a/generic/tclThreadAlloc.c
+++ b/generic/tclThreadAlloc.c
@@ -538,7 +538,7 @@ TclThreadAllocObj(void)
cachePtr->numObjects = numMove = NOBJALLOC;
newObjsPtr = (Tcl_Obj *)TclpSysAlloc(sizeof(Tcl_Obj) * numMove);
if (newObjsPtr == NULL) {
- Tcl_Panic("alloc: could not allocate %ld new objects", numMove);
+ Tcl_Panic("alloc: could not allocate %" TCL_Z_MODIFIER "u new objects", numMove);
}
cachePtr->lastPtr = newObjsPtr + numMove - 1;
objPtr = cachePtr->firstObjPtr; /* NULL */
diff --git a/generic/tclTomMath.decls b/generic/tclTomMath.decls
index ea310e0..9c5ca8b 100644
--- a/generic/tclTomMath.decls
+++ b/generic/tclTomMath.decls
@@ -17,7 +17,6 @@ library tcl
# Define the unsupported generic interfaces.
interface tclTomMath
-# hooks {tclTomMathInt}
scspec EXTERN
# Declare each of the functions in the Tcl tommath interface
diff --git a/generic/tclUtf.c b/generic/tclUtf.c
index fde30c5..9e49e87 100644
--- a/generic/tclUtf.c
+++ b/generic/tclUtf.c
@@ -64,20 +64,12 @@ static const unsigned char totalBytes[256] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-/* Tcl_UtfCharComplete() might point to 2nd byte of valid 4-byte sequence */
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
-/* End of "continuation byte section" */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
-#if TCL_UTF_MAX > 3
- 4,4,4,4,4,
-#else
- 1,1,1,1,1,
-#endif
- 1,1,1,1,1,1,1,1,1,1,1
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1
};
-
+
static const unsigned char complete[256] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
@@ -88,15 +80,9 @@ static const unsigned char complete[256] = {
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
/* End of "continuation byte section" */
2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
-#if TCL_UTF_MAX > 3
- 4,4,4,4,4,
-#else
- 3,3,3,3,3,
-#endif
- 1,1,1,1,1,1,1,1,1,1,1
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1
};
-
+
/*
* Functions used only in this module.
*/
@@ -695,7 +681,7 @@ Tcl_UtfToUniCharDString(
p += TclUtfToUCS4(p, &ch);
*w++ = ch;
}
- while ((p < endPtr) && TclUCS4Complete(p, endPtr-p)) {
+ while ((p < endPtr) && Tcl_UtfCharComplete(p, endPtr-p)) {
p += TclUtfToUCS4(p, &ch);
*w++ = ch;
}
@@ -753,7 +739,7 @@ Tcl_UtfToChar16DString(
*w++ = ch;
}
while (p < endPtr) {
- if (TclChar16Complete(p, endPtr-p)) {
+ if (Tcl_UtfCharComplete(p, endPtr-p)) {
p += Tcl_UtfToChar16(p, &ch);
*w++ = ch;
} else {
@@ -834,7 +820,7 @@ Tcl_NumUtfChars(
/* Pointer to the end of string. Never read endPtr[0] */
const char *endPtr = src + length;
/* Pointer to last byte where optimization still can be used */
- const char *optPtr = endPtr - TCL_UTF_MAX;
+ const char *optPtr = endPtr - 4;
/*
* Optimize away the call in this loop. Justified because...
@@ -971,6 +957,10 @@ Tcl_UtfNext(
const char *next;
if (((*src) & 0xC0) == 0x80) {
+ /* Continuation byte, so we start 'inside' a (possible valid) UTF-8
+ * sequence. Since we are not allowed to access src[-1], we cannot
+ * check if the sequence is actually valid, the best we can do is
+ * just assume it is valid and locate the end. */
if ((((*++src) & 0xC0) == 0x80) && (((*++src) & 0xC0) == 0x80)) {
++src;
}
@@ -1065,7 +1055,7 @@ Tcl_UtfPrev(
* it (the fallback) is correct.
*/
- || (trailBytesSeen >= complete[byte])) {
+ || (trailBytesSeen >= totalBytes[byte])) {
/*
* That is, (1 + trailBytesSeen > needed).
* We've examined more bytes than needed to complete
@@ -1106,19 +1096,14 @@ Tcl_UtfPrev(
/* Continue the search backwards... */
look--;
- } while (trailBytesSeen < TCL_UTF_MAX);
+ } while (trailBytesSeen < 4);
/*
- * We've seen TCL_UTF_MAX trail bytes, so we know there will not be a
+ * We've seen 4 trail bytes, so we know there will not be a
* properly formed byte sequence to find, and we can stop looking,
- * accepting the fallback (for TCL_UTF_MAX > 3) or just go back as
- * far as we can.
+ * accepting the fallback.
*/
-#if TCL_UTF_MAX > 3
return fallback;
-#else
- return src - TCL_UTF_MAX;
-#endif
}
/*
@@ -1751,7 +1736,7 @@ Tcl_UniCharToLower(
/* Clear away extension bits, if any */
return ch & 0x1FFFFF;
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -1845,7 +1830,7 @@ TclUniCharNcmp(
const Tcl_UniChar *uct, /* Unicode string ucs is compared to. */
size_t numChars) /* Number of unichars to compare. */
{
-#ifdef WORDS_BIGENDIAN
+#if defined(WORDS_BIGENDIAN) && (TCL_UTF_MAX > 3)
/*
* We are definitely on a big-endian machine; memcmp() is safe
*/
@@ -1859,6 +1844,14 @@ TclUniCharNcmp(
for ( ; numChars != 0; ucs++, uct++, numChars--) {
if (*ucs != *uct) {
+#if TCL_UTF_MAX < 4
+ /* special case for handling upper surrogates */
+ if (((*ucs & 0xFC00) == 0xD800) && ((*uct & 0xFC00) != 0xD800)) {
+ return 1;
+ } else if (((*uct & 0xFC00) == 0xD800)) {
+ return -1;
+ }
+#endif
return (*ucs - *uct);
}
}
@@ -1896,6 +1889,14 @@ TclUniCharNcasecmp(
Tcl_UniChar lct = Tcl_UniCharToLower(*uct);
if (lcs != lct) {
+#if TCL_UTF_MAX < 4
+ /* special case for handling upper surrogates */
+ if (((lcs & 0xFC00) == 0xD800) && ((lct & 0xFC00) != 0xD800)) {
+ return 1;
+ } else if (((lct & 0xFC00) == 0xD800)) {
+ return -1;
+ }
+#endif
return (lcs - lct);
}
}
diff --git a/generic/tclUtil.c b/generic/tclUtil.c
index 55b12ac..c9c3b8f 100644
--- a/generic/tclUtil.c
+++ b/generic/tclUtil.c
@@ -1655,11 +1655,7 @@ TclTrimRight(
const char *q = trim;
size_t pInc = 0, bytesLeft = numTrim;
- pp = TclUtfPrev(p, bytes);
-#if TCL_UTF_MAX < 4 /* Needed because TclUtfPrev() cannot always jump back */
- /* sufficiently. See [d43f96c1a8] */
- pp = TclUtfPrev(pp, bytes);
-#endif
+ pp = Tcl_UtfPrev(p, bytes);
do {
pp += pInc;
pInc = TclUtfToUCS4(pp, &ch1);
diff --git a/generic/tclZipfs.c b/generic/tclZipfs.c
index 2089ef9..21a3b01 100644
--- a/generic/tclZipfs.c
+++ b/generic/tclZipfs.c
@@ -47,6 +47,7 @@
#define ZIPFS_VOLUME_LEN 9
#define ZIPFS_APP_MOUNT "//zipfs:/app"
#define ZIPFS_ZIP_MOUNT "//zipfs:/lib/tcl"
+#define ZIPFS_FALLBACK_ENCODING "cp437"
/*
* Various constants and offsets found in ZIP archive files
@@ -129,6 +130,14 @@
Tcl_SetObjResult(interp, Tcl_NewStringObj(errstr, -1)); \
} \
} while (0)
+#define ZIPFS_MEM_ERROR(interp) \
+ do { \
+ if (interp) { \
+ Tcl_SetObjResult(interp, Tcl_NewStringObj( \
+ "out of memory", -1)); \
+ Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL); \
+ } \
+ } while (0)
#define ZIPFS_POSIX_ERROR(interp,errstr) \
do { \
if (interp) { \
@@ -136,27 +145,11 @@
"%s: %s", errstr, Tcl_PosixError(interp))); \
} \
} while (0)
-
-/*
- * Macros to read and write 16 and 32 bit integers from/to ZIP archives.
- */
-
-#define ZipReadInt(p) \
- ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
-#define ZipReadShort(p) \
- ((p)[0] | ((p)[1] << 8))
-
-#define ZipWriteInt(p, v) \
- do { \
- (p)[0] = (v) & 0xff; \
- (p)[1] = ((v) >> 8) & 0xff; \
- (p)[2] = ((v) >> 16) & 0xff; \
- (p)[3] = ((v) >> 24) & 0xff; \
- } while (0)
-#define ZipWriteShort(p, v) \
- do { \
- (p)[0] = (v) & 0xff; \
- (p)[1] = ((v) >> 8) & 0xff; \
+#define ZIPFS_ERROR_CODE(interp,errcode) \
+ do { \
+ if (interp) { \
+ Tcl_SetErrorCode(interp, "TCL", "ZIPFS", errcode, NULL); \
+ } \
} while (0)
/*
@@ -177,6 +170,12 @@ TCL_DECLARE_MUTEX(localtimeMutex)
#endif /* !_WIN32 && !HAVE_LOCALTIME_R && TCL_THREADS */
/*
+ * Forward declaration.
+ */
+
+struct ZipEntry;
+
+/*
* In-core description of mounted ZIP archive file.
*/
@@ -205,6 +204,7 @@ typedef struct ZipFile {
/*
* In-core description of file contained in mounted ZIP archive.
+ * ZIP_ATTR_
*/
typedef struct ZipEntry {
@@ -260,12 +260,22 @@ 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 */
+ int wrmax; /* Maximum write size of a file; only written
+ * to from Tcl code in a trusted interpreter,
+ * so NOT protected by mutex. */
+ char *fallbackEntryEncoding;/* The fallback encoding for ZIP entries when
+ * they are believed to not be UTF-8; only
+ * written to from Tcl code in a trusted
+ * interpreter, so not protected by mutex. */
+ Tcl_Encoding utf8; /* The UTF-8 encoding that we prefer to use
+ * for the strings (especially filenames)
+ * embedded in a ZIP. Other encodings are used
+ * dynamically. */
int idCount; /* Counter for channel names */
Tcl_HashTable fileHash; /* File name to ZipEntry mapping */
Tcl_HashTable zipHash; /* Mount to ZipFile mapping */
} ZipFS = {
- 0, 0, 0, DEFAULT_WRITE_MAX_SIZE, 0,
+ 0, 0, 0, DEFAULT_WRITE_MAX_SIZE, NULL, NULL, 0,
{0,{0,0,0,0},0,0,0,0,0,0,0,0,0},
{0,{0,0,0,0},0,0,0,0,0,0,0,0,0}
};
@@ -282,9 +292,30 @@ static const char *zipfs_literal_tcl_library = NULL;
/* Function prototypes */
+static int CopyImageFile(Tcl_Interp *interp, const char *imgName,
+ Tcl_Channel out);
static inline int DescribeMounted(Tcl_Interp *interp,
const char *mountPoint);
+static int InitReadableChannel(Tcl_Interp *interp,
+ ZipChannel *info, ZipEntry *z);
+static int InitWritableChannel(Tcl_Interp *interp,
+ ZipChannel *info, ZipEntry *z, int trunc);
static inline int ListMountPoints(Tcl_Interp *interp);
+static void SerializeCentralDirectoryEntry(
+ const unsigned char *start,
+ const unsigned char *end, unsigned char *buf,
+ ZipEntry *z, size_t nameLength,
+ long long dataStartOffset);
+static void SerializeCentralDirectorySuffix(
+ const unsigned char *start,
+ const unsigned char *end, unsigned char *buf,
+ int entryCount, long long dataStartOffset,
+ long long directoryStartOffset,
+ long long suffixStartOffset);
+static void SerializeLocalEntryHeader(
+ const unsigned char *start,
+ const unsigned char *end, unsigned char *buf,
+ ZipEntry *z, int nameLength, int align);
#if !defined(STATIC_BUILD)
static int ZipfsAppHookFindTclInit(const char *archive);
#endif
@@ -299,6 +330,9 @@ static Tcl_Channel ZipFSOpenFileChannelProc(Tcl_Interp *interp,
static int ZipFSMatchInDirectoryProc(Tcl_Interp *interp,
Tcl_Obj *result, Tcl_Obj *pathPtr,
const char *pattern, Tcl_GlobTypeData *types);
+static void ZipFSMatchMountPoints(Tcl_Obj *result,
+ Tcl_Obj *normPathPtr, const char *pattern,
+ Tcl_DString *prefix);
static Tcl_Obj * ZipFSListVolumesProc(void);
static const char *const *ZipFSFileAttrStringsProc(Tcl_Obj *pathPtr,
Tcl_Obj **objPtrRef);
@@ -309,6 +343,8 @@ static int ZipFSFileAttrsSetProc(Tcl_Interp *interp, int index,
static int ZipFSLoadFile(Tcl_Interp *interp, Tcl_Obj *path,
Tcl_LoadHandle *loadHandle,
Tcl_FSUnloadFileProc **unloadProcPtr, int flags);
+static int ZipMapArchive(Tcl_Interp *interp, ZipFile *zf,
+ void *handle);
static void ZipfsExitHandler(ClientData clientData);
static void ZipfsSetup(void);
static int ZipChannelClose(void *instanceData,
@@ -316,8 +352,12 @@ static int ZipChannelClose(void *instanceData,
static Tcl_DriverGetHandleProc ZipChannelGetFile;
static int ZipChannelRead(void *instanceData, char *buf,
int toRead, int *errloc);
-static long long ZipChannelWideSeek(void *instanceData, long long offset,
+#if !defined(TCL_NO_DEPRECATED) && (TCL_MAJOR_VERSION < 9)
+static int ZipChannelSeek(void *instanceData, long offset,
int mode, int *errloc);
+#endif
+static long long ZipChannelWideSeek(void *instanceData,
+ long long offset, int mode, int *errloc);
static void ZipChannelWatchChannel(void *instanceData,
int mask);
static int ZipChannelWrite(void *instanceData,
@@ -356,7 +396,7 @@ static const Tcl_Filesystem zipfsFilesystem = {
NULL, /* renameFileProc */
NULL, /* copyDirectoryProc */
NULL, /* lstatProc */
- (Tcl_FSLoadFileProc *)(void *)ZipFSLoadFile,
+ (Tcl_FSLoadFileProc *) (void *) ZipFSLoadFile,
NULL, /* getCwdProc */
NULL, /* chdirProc */
};
@@ -366,24 +406,108 @@ static const Tcl_Filesystem zipfsFilesystem = {
*/
static Tcl_ChannelType ZipChannelType = {
- "zip", /* Type name. */
+ "zip", /* Type name. */
TCL_CHANNEL_VERSION_5,
- NULL, /* Close channel, clean instance data */
- ZipChannelRead, /* Handle read request */
- ZipChannelWrite, /* Handle write request */
- NULL, /* 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 */
- ZipChannelClose, /* 2nd version of close channel, NULL'able */
- NULL, /* Set blocking mode for raw channel, NULL'able */
- NULL, /* Function to flush channel, NULL'able */
- NULL, /* Function to handle event, NULL'able */
- ZipChannelWideSeek, /* Wide seek function, NULL'able */
- NULL, /* Thread action function, NULL'able */
- NULL, /* Truncate function, NULL'able */
+ TCL_CLOSE2PROC, /* Close channel, clean instance data */
+ ZipChannelRead, /* Handle read request */
+ ZipChannelWrite, /* Handle write request */
+#if !defined(TCL_NO_DEPRECATED) && (TCL_MAJOR_VERSION < 9)
+ ZipChannelSeek, /* Move location of access point, NULL'able */
+#else
+ NULL, /* Move location of access point, NULL'able */
+#endif
+ NULL, /* Set options, NULL'able */
+ NULL, /* Get options, NULL'able */
+ ZipChannelWatchChannel, /* Initialize notifier */
+ ZipChannelGetFile, /* Get OS handle from the channel */
+ ZipChannelClose, /* 2nd version of close channel, NULL'able */
+ NULL, /* Set blocking mode for raw channel,
+ * NULL'able */
+ NULL, /* Function to flush channel, NULL'able */
+ NULL, /* Function to handle event, NULL'able */
+ ZipChannelWideSeek, /* Wide seek function, NULL'able */
+ NULL, /* Thread action function, NULL'able */
+ NULL, /* Truncate function, NULL'able */
};
+
+/*
+ * Miscellaneous constants.
+ */
+
+#define ERROR_LENGTH ((size_t) -1)
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * ZipReadInt, ZipReadShort, ZipWriteInt, ZipWriteShort --
+ *
+ * Inline functions to read and write little-endian 16 and 32 bit
+ * integers from/to buffers representing parts of ZIP archives.
+ *
+ * These take bufferStart and bufferEnd pointers, which are used to
+ * maintain a guarantee that out-of-bounds accesses don't happen when
+ * reading or writing critical directory structures.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static inline unsigned int
+ZipReadInt(
+ const unsigned char *bufferStart,
+ const unsigned char *bufferEnd,
+ const unsigned char *ptr)
+{
+ if (ptr < bufferStart || ptr + 4 > bufferEnd) {
+ Tcl_Panic("out of bounds read(4): start=%p, end=%p, ptr=%p",
+ bufferStart, bufferEnd, ptr);
+ }
+ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+}
+
+static inline unsigned short
+ZipReadShort(
+ const unsigned char *bufferStart,
+ const unsigned char *bufferEnd,
+ const unsigned char *ptr)
+{
+ if (ptr < bufferStart || ptr + 2 > bufferEnd) {
+ Tcl_Panic("out of bounds read(2): start=%p, end=%p, ptr=%p",
+ bufferStart, bufferEnd, ptr);
+ }
+ return ptr[0] | (ptr[1] << 8);
+}
+
+static inline void
+ZipWriteInt(
+ const unsigned char *bufferStart,
+ const unsigned char *bufferEnd,
+ unsigned char *ptr,
+ unsigned int value)
+{
+ if (ptr < bufferStart || ptr + 4 > bufferEnd) {
+ Tcl_Panic("out of bounds write(4): start=%p, end=%p, ptr=%p",
+ bufferStart, bufferEnd, ptr);
+ }
+ ptr[0] = value & 0xff;
+ ptr[1] = (value >> 8) & 0xff;
+ ptr[2] = (value >> 16) & 0xff;
+ ptr[3] = (value >> 24) & 0xff;
+}
+
+static inline void
+ZipWriteShort(
+ const unsigned char *bufferStart,
+ const unsigned char *bufferEnd,
+ unsigned char *ptr,
+ unsigned short value)
+{
+ if (ptr < bufferStart || ptr + 2 > bufferEnd) {
+ Tcl_Panic("out of bounds write(2): start=%p, end=%p, ptr=%p",
+ bufferStart, bufferEnd, ptr);
+ }
+ ptr[0] = value & 0xff;
+ ptr[1] = (value >> 8) & 0xff;
+}
/*
*-------------------------------------------------------------------------
@@ -406,7 +530,7 @@ TCL_DECLARE_MUTEX(ZipFSMutex)
static Tcl_Condition ZipFSCond;
-static void
+static inline void
ReadLock(void)
{
Tcl_MutexLock(&ZipFSMutex);
@@ -419,7 +543,7 @@ ReadLock(void)
Tcl_MutexUnlock(&ZipFSMutex);
}
-static void
+static inline void
WriteLock(void)
{
Tcl_MutexLock(&ZipFSMutex);
@@ -432,7 +556,7 @@ WriteLock(void)
Tcl_MutexUnlock(&ZipFSMutex);
}
-static void
+static inline void
Unlock(void)
{
Tcl_MutexLock(&ZipFSMutex);
@@ -552,7 +676,7 @@ ToDosDate(
*-------------------------------------------------------------------------
*/
-static int
+static inline int
CountSlashes(
const char *string)
{
@@ -571,6 +695,115 @@ CountSlashes(
/*
*-------------------------------------------------------------------------
*
+ * DecodeZipEntryText --
+ *
+ * Given a sequence of bytes from an entry in a ZIP central directory,
+ * convert that into a Tcl string. This is complicated because we don't
+ * actually know what encoding is in use! So we try to use UTF-8, and if
+ * that goes wrong, we fall back to a user-specified encoding, or to an
+ * encoding we specify (Windows code page 437), or to ISO 8859-1 if
+ * absolutely nothing else works.
+ *
+ * During Tcl startup, we skip the user-specified encoding and cp437, as
+ * we may well not have any loadable encodings yet. Tcl's own library
+ * files ought to be using ASCII filenames.
+ *
+ * Results:
+ * The decoded filename; the filename is owned by the argument DString.
+ *
+ * Side effects:
+ * Updates dstPtr.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static char *
+DecodeZipEntryText(
+ const unsigned char *inputBytes,
+ unsigned int inputLength,
+ Tcl_DString *dstPtr)
+{
+ Tcl_Encoding encoding;
+ const char *src;
+ char *dst;
+ int dstLen, srcLen = inputLength, flags;
+ Tcl_EncodingState state;
+
+ Tcl_DStringInit(dstPtr);
+ if (inputLength < 1) {
+ return Tcl_DStringValue(dstPtr);
+ }
+
+ /*
+ * We can't use Tcl_ExternalToUtfDString at this point; it has no way to
+ * fail. So we use this modified version of it that can report encoding
+ * errors to us (so we can fall back to something else).
+ *
+ * The utf-8 encoding is implemented internally, and so is guaranteed to
+ * be present.
+ */
+
+ src = (const char *) inputBytes;
+ dst = Tcl_DStringValue(dstPtr);
+ dstLen = dstPtr->spaceAvl - 1;
+ flags = TCL_ENCODING_START | TCL_ENCODING_END |
+ TCL_ENCODING_STOPONERROR; /* Special flag! */
+
+ while (1) {
+ int srcRead, dstWrote;
+ int result = Tcl_ExternalToUtf(NULL, ZipFS.utf8, src, srcLen, flags,
+ &state, dst, dstLen, &srcRead, &dstWrote, NULL);
+ int soFar = dst + dstWrote - Tcl_DStringValue(dstPtr);
+
+ if (result == TCL_OK) {
+ Tcl_DStringSetLength(dstPtr, soFar);
+ return Tcl_DStringValue(dstPtr);
+ } else if (result != TCL_CONVERT_NOSPACE) {
+ break;
+ }
+
+ flags &= ~TCL_ENCODING_START;
+ src += srcRead;
+ srcLen -= srcRead;
+ if (Tcl_DStringLength(dstPtr) == 0) {
+ Tcl_DStringSetLength(dstPtr, dstLen);
+ }
+ Tcl_DStringSetLength(dstPtr, 2 * Tcl_DStringLength(dstPtr) + 1);
+ dst = Tcl_DStringValue(dstPtr) + soFar;
+ dstLen = Tcl_DStringLength(dstPtr) - soFar - 1;
+ }
+
+ /*
+ * Something went wrong. Fall back to another encoding. Those *can* use
+ * Tcl_ExternalToUtfDString().
+ */
+
+ encoding = NULL;
+ if (ZipFS.fallbackEntryEncoding) {
+ encoding = Tcl_GetEncoding(NULL, ZipFS.fallbackEntryEncoding);
+ }
+ if (!encoding) {
+ encoding = Tcl_GetEncoding(NULL, ZIPFS_FALLBACK_ENCODING);
+ }
+ if (!encoding) {
+ /*
+ * Fallback to internal encoding that always converts all bytes.
+ * Should only happen when a filename isn't UTF-8 and we've not got
+ * our encodings initialised for some reason.
+ */
+
+ encoding = Tcl_GetEncoding(NULL, "iso8859-1");
+ }
+
+ char *converted = Tcl_ExternalToUtfDString(encoding,
+ (const char *) inputBytes, inputLength, dstPtr);
+ Tcl_FreeEncoding(encoding);
+ return converted;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
* CanonicalPath --
*
* This function computes the canonical path from a directory and file
@@ -756,16 +989,16 @@ CanonicalPath(
*-------------------------------------------------------------------------
*/
-static ZipEntry *
+static inline ZipEntry *
ZipFSLookup(
- char *filename)
+ const char *filename)
{
Tcl_HashEntry *hPtr;
ZipEntry *z = NULL;
hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, filename);
if (hPtr) {
- z = (ZipEntry *)Tcl_GetHashValue(hPtr);
+ z = (ZipEntry *) Tcl_GetHashValue(hPtr);
}
return z;
}
@@ -773,13 +1006,13 @@ ZipFSLookup(
/*
*-------------------------------------------------------------------------
*
- * ZipFSLookupMount --
+ * ZipFSLookupZip --
*
- * This function returns an indication if the given file name corresponds
- * to a mounted ZIP archive file.
+ * This function gets the structure for a mounted ZIP archive.
*
* Results:
- * Returns true, if the given file name is a mounted ZIP archive file.
+ * Returns a pointer to the structure, or NULL if the file is ZIP file is
+ * unknown/not mounted.
*
* Side effects:
* None.
@@ -787,25 +1020,76 @@ ZipFSLookup(
*-------------------------------------------------------------------------
*/
-#ifdef NEVER_USED
-static int
-ZipFSLookupMount(
- char *filename)
+static inline ZipFile *
+ZipFSLookupZip(
+ const char *mountPoint)
{
Tcl_HashEntry *hPtr;
- Tcl_HashSearch search;
+ ZipFile *zf = NULL;
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- ZipFile *zf = Tcl_GetHashValue(hPtr);
+ hPtr = Tcl_FindHashEntry(&ZipFS.zipHash, mountPoint);
+ if (hPtr) {
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
+ }
+ return zf;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * AllocateZipFile, AllocateZipEntry, AllocateZipChannel --
+ *
+ * Allocates the memory for a datastructure. Always ensures that it is
+ * zeroed out for safety.
+ *
+ * Returns:
+ * The allocated structure, or NULL if allocate fails.
+ *
+ * Side effects:
+ * The interpreter result may be written to on error. Which might fail
+ * (for ZipFile) in a low-memory situation. Always panics if ZipEntry
+ * allocation fails.
+ *
+ *-------------------------------------------------------------------------
+ */
- if (strcmp(zf->mountPoint, filename) == 0) {
- return 1;
- }
+static inline ZipFile *
+AllocateZipFile(
+ Tcl_Interp *interp,
+ size_t mountPointNameLength)
+{
+ size_t size = sizeof(ZipFile) + mountPointNameLength + 1;
+ ZipFile *zf = (ZipFile *) Tcl_AttemptAlloc(size);
+
+ if (!zf) {
+ ZIPFS_MEM_ERROR(interp);
+ } else {
+ memset(zf, 0, size);
}
- return 0;
+ return zf;
+}
+
+static inline ZipEntry *
+AllocateZipEntry(void)
+{
+ ZipEntry *z = (ZipEntry *) Tcl_Alloc(sizeof(ZipEntry));
+ memset(z, 0, sizeof(ZipEntry));
+ return z;
+}
+
+static inline ZipChannel *
+AllocateZipChannel(
+ Tcl_Interp *interp)
+{
+ ZipChannel *zc = (ZipChannel *) Tcl_AttemptAlloc(sizeof(ZipChannel));
+
+ if (!zc) {
+ ZIPFS_MEM_ERROR(interp);
+ } else {
+ memset(zc, 0, sizeof(ZipChannel));
+ }
+ return zc;
}
-#endif /* NEVER_USED */
/*
*-------------------------------------------------------------------------
@@ -842,6 +1126,10 @@ ZipFSCloseArchive(
return;
}
+ /*
+ * Remove the memory mapping, if we have one.
+ */
+
#ifdef _WIN32
if (zf->data && !zf->ptrToFree) {
UnmapViewOfFile(zf->data);
@@ -853,7 +1141,7 @@ ZipFSCloseArchive(
#else /* !_WIN32 */
if ((zf->data != MAP_FAILED) && !zf->ptrToFree) {
munmap(zf->data, zf->length);
- zf->data = (unsigned char *)MAP_FAILED;
+ zf->data = (unsigned char *) MAP_FAILED;
}
#endif /* _WIN32 */
@@ -862,7 +1150,7 @@ ZipFSCloseArchive(
zf->ptrToFree = NULL;
}
if (zf->chan) {
- Tcl_CloseEx(interp, zf->chan, 0);
+ Tcl_Close(interp, zf->chan);
zf->chan = NULL;
}
}
@@ -874,7 +1162,7 @@ ZipFSCloseArchive(
*
* This function takes a memory mapped zip file and indexes the contents.
* When "needZip" is zero an embedded ZIP archive in an executable file
- * is accepted.
+ * is accepted. Note that we do not support ZIP64.
*
* Results:
* TCL_OK on success, TCL_ERROR otherwise with an error message placed
@@ -894,12 +1182,20 @@ ZipFSFindTOC(
ZipFile *zf)
{
size_t i;
- unsigned char *p, *q;
+ const unsigned char *p, *q;
+ const unsigned char *start = zf->data;
+ const unsigned char *end = zf->data + zf->length;
+
+ /*
+ * Scan backwards from the end of the file for the signature. This is
+ * necessary because ZIP archives aren't the only things that get tagged
+ * on the end of executables; digital signatures can also go there.
+ */
p = zf->data + zf->length - ZIP_CENTRAL_END_LEN;
- while (p >= zf->data) {
+ while (p >= start) {
if (*p == (ZIP_CENTRAL_END_SIG & 0xFF)) {
- if (ZipReadInt(p) == ZIP_CENTRAL_END_SIG) {
+ if (ZipReadInt(start, end, p) == ZIP_CENTRAL_END_SIG) {
break;
}
p -= ZIP_SIG_LEN;
@@ -908,73 +1204,93 @@ ZipFSFindTOC(
}
}
if (p < zf->data) {
+ /*
+ * Didn't find it (or not enough space for a central directory!); not
+ * a ZIP archive. This might be OK or a problem.
+ */
+
if (!needZip) {
zf->baseOffset = zf->passOffset = zf->length;
return TCL_OK;
}
ZIPFS_ERROR(interp, "wrong end signature");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "END_SIG", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "END_SIG");
goto error;
}
- zf->numFiles = ZipReadShort(p + ZIP_CENTRAL_ENTS_OFFS);
+
+ /*
+ * How many files in the archive? If that's bogus, we're done here.
+ */
+
+ zf->numFiles = ZipReadShort(start, end, p + ZIP_CENTRAL_ENTS_OFFS);
if (zf->numFiles == 0) {
if (!needZip) {
zf->baseOffset = zf->passOffset = zf->length;
return TCL_OK;
}
ZIPFS_ERROR(interp, "empty archive");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "EMPTY", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "EMPTY");
goto error;
}
- q = zf->data + ZipReadInt(p + ZIP_CENTRAL_DIRSTART_OFFS);
- p -= ZipReadInt(p + ZIP_CENTRAL_DIRSIZE_OFFS);
- if ((p < zf->data) || (p > zf->data + zf->length)
+
+ /*
+ * Where does the central directory start?
+ */
+
+ q = zf->data + ZipReadInt(start, end, p + ZIP_CENTRAL_DIRSTART_OFFS);
+ p -= ZipReadInt(start, end, p + ZIP_CENTRAL_DIRSIZE_OFFS);
+ if ((p < q) || (p < zf->data) || (p > zf->data + zf->length)
|| (q < zf->data) || (q > zf->data + zf->length)) {
if (!needZip) {
zf->baseOffset = zf->passOffset = zf->length;
return TCL_OK;
}
ZIPFS_ERROR(interp, "archive directory not found");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "NO_DIR", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "NO_DIR");
goto error;
}
+
+ /*
+ * Read the central directory.
+ */
+
zf->baseOffset = zf->passOffset = p - q;
zf->directoryOffset = p - zf->data;
q = p;
for (i = 0; i < zf->numFiles; i++) {
int pathlen, comlen, extra;
- if (q + ZIP_CENTRAL_HEADER_LEN > zf->data + zf->length) {
+ if (q + ZIP_CENTRAL_HEADER_LEN > end) {
ZIPFS_ERROR(interp, "wrong header length");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "HDR_LEN", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "HDR_LEN");
goto error;
}
- if (ZipReadInt(q) != ZIP_CENTRAL_HEADER_SIG) {
+ if (ZipReadInt(start, end, q) != ZIP_CENTRAL_HEADER_SIG) {
ZIPFS_ERROR(interp, "wrong header signature");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "HDR_SIG", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "HDR_SIG");
goto error;
}
- pathlen = ZipReadShort(q + ZIP_CENTRAL_PATHLEN_OFFS);
- comlen = ZipReadShort(q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
- extra = ZipReadShort(q + ZIP_CENTRAL_EXTRALEN_OFFS);
+ pathlen = ZipReadShort(start, end, q + ZIP_CENTRAL_PATHLEN_OFFS);
+ comlen = ZipReadShort(start, end, q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
+ extra = ZipReadShort(start, end, q + ZIP_CENTRAL_EXTRALEN_OFFS);
q += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN;
}
+
+ /*
+ * If there's also an encoded password, extract that too (but don't decode
+ * yet).
+ */
+
q = zf->data + zf->baseOffset;
- if ((zf->baseOffset >= 6) && (ZipReadInt(q - 4) == ZIP_PASSWORD_END_SIG)) {
+ if ((zf->baseOffset >= 6) &&
+ (ZipReadInt(start, end, q - 4) == ZIP_PASSWORD_END_SIG)) {
+ const unsigned char *passPtr;
+
i = q[-5];
- if (q - 5 - i > zf->data) {
+ passPtr = q - 5 - i;
+ if (passPtr >= start && passPtr + i < end) {
zf->passBuf[0] = i;
- memcpy(zf->passBuf + 1, q - 5 - i, i);
+ memcpy(zf->passBuf + 1, passPtr, i);
zf->passOffset -= i ? (5 + i) : 0;
}
}
@@ -1023,41 +1339,60 @@ ZipFSOpenArchive(
zf->data = NULL;
zf->mountHandle = INVALID_HANDLE_VALUE;
#else /* !_WIN32 */
- zf->data = (unsigned char *)MAP_FAILED;
+ zf->data = (unsigned char *) MAP_FAILED;
#endif /* _WIN32 */
zf->length = 0;
zf->numFiles = 0;
zf->baseOffset = zf->passOffset = 0;
zf->ptrToFree = NULL;
zf->passBuf[0] = 0;
+
+ /*
+ * Actually open the file.
+ */
+
zf->chan = Tcl_OpenFileChannel(interp, zipname, "rb", 0);
if (!zf->chan) {
return TCL_ERROR;
}
- if (Tcl_GetChannelHandle(zf->chan, TCL_READABLE, &handle) != TCL_OK) {
+
+ /*
+ * See if we can get the OS handle. If we can, we can use that to memory
+ * map the file, which is nice and efficient. However, it totally depends
+ * on the filename pointing to a real regular OS file.
+ *
+ * Opening real filesystem entities that are not files will lead to an
+ * error.
+ */
+
+ if (Tcl_GetChannelHandle(zf->chan, TCL_READABLE, &handle) == TCL_OK) {
+ if (ZipMapArchive(interp, zf, handle) != TCL_OK) {
+ goto error;
+ }
+ } else {
+ /*
+ * Not an OS file, but rather something in a Tcl VFS. Must copy into
+ * memory.
+ */
+
zf->length = Tcl_Seek(zf->chan, 0, SEEK_END);
- if (zf->length == TCL_IO_FAILURE) {
+ if (zf->length == ERROR_LENGTH) {
ZIPFS_POSIX_ERROR(interp, "seek error");
goto error;
}
if ((zf->length - ZIP_CENTRAL_END_LEN)
> (64 * 1024 * 1024 - ZIP_CENTRAL_END_LEN)) {
ZIPFS_ERROR(interp, "illegal file size");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "FILE_SIZE", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "FILE_SIZE");
goto error;
}
if (Tcl_Seek(zf->chan, 0, SEEK_SET) == -1) {
ZIPFS_POSIX_ERROR(interp, "seek error");
goto error;
}
- zf->ptrToFree = zf->data = (unsigned char *)Tcl_AttemptAlloc(zf->length);
+ zf->ptrToFree = zf->data = (unsigned char *) Tcl_AttemptAlloc(zf->length);
if (!zf->ptrToFree) {
- ZIPFS_ERROR(interp, "out of memory");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
+ ZIPFS_MEM_ERROR(interp);
goto error;
}
i = Tcl_Read(zf->chan, (char *) zf->data, zf->length);
@@ -1065,69 +1400,139 @@ ZipFSOpenArchive(
ZIPFS_POSIX_ERROR(interp, "file read error");
goto error;
}
- Tcl_CloseEx(interp, zf->chan, 0);
+ Tcl_Close(interp, zf->chan);
zf->chan = NULL;
- } else {
+ }
+ return ZipFSFindTOC(interp, needZip, zf);
+
+ /*
+ * Handle errors by closing the archive. This includes closing the channel
+ * handle for the archive file.
+ */
+
+ error:
+ ZipFSCloseArchive(interp, zf);
+ return TCL_ERROR;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * ZipMapArchive --
+ *
+ * Wrapper around the platform-specific parts of mmap() (and Windows's
+ * equivalent) because it's not part of the standard channel API.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int
+ZipMapArchive(
+ Tcl_Interp *interp, /* Interpreter for error reporting. */
+ ZipFile *zf, /* The archive descriptor structure. */
+ void *handle) /* The OS handle to the open archive. */
+{
#ifdef _WIN32
- int readSuccessful;
+ HANDLE hFile = (HANDLE) handle;
+ int readSuccessful;
+
+ /*
+ * Determine the file size.
+ */
+
# ifdef _WIN64
- i = GetFileSizeEx((HANDLE) handle, (PLARGE_INTEGER) &zf->length);
- readSuccessful = (i != 0);
+ readSuccessful = GetFileSizeEx(hFile, (PLARGE_INTEGER) &zf->length) != 0;
# else /* !_WIN64 */
- zf->length = GetFileSize((HANDLE) handle, 0);
- readSuccessful = (zf->length != (size_t) INVALID_FILE_SIZE);
+ zf->length = GetFileSize(hFile, 0);
+ readSuccessful = (zf->length != (size_t) INVALID_FILE_SIZE);
# endif /* _WIN64 */
- if (!readSuccessful || (zf->length < ZIP_CENTRAL_END_LEN)) {
- ZIPFS_POSIX_ERROR(interp, "invalid file size");
- goto error;
- }
- zf->mountHandle = CreateFileMappingW((HANDLE) handle, 0, PAGE_READONLY,
- 0, zf->length, 0);
- if (zf->mountHandle == INVALID_HANDLE_VALUE) {
- ZIPFS_POSIX_ERROR(interp, "file mapping failed");
- goto error;
- }
- zf->data = (unsigned char *)MapViewOfFile(zf->mountHandle, FILE_MAP_READ, 0, 0,
- zf->length);
- if (!zf->data) {
- ZIPFS_POSIX_ERROR(interp, "file mapping failed");
- goto error;
- }
+ if (!readSuccessful || (zf->length < ZIP_CENTRAL_END_LEN)) {
+ ZIPFS_POSIX_ERROR(interp, "invalid file size");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Map the file.
+ */
+
+ zf->mountHandle = CreateFileMappingW(hFile, 0, PAGE_READONLY, 0,
+ zf->length, 0);
+ if (zf->mountHandle == INVALID_HANDLE_VALUE) {
+ ZIPFS_POSIX_ERROR(interp, "file mapping failed");
+ return TCL_ERROR;
+ }
+ zf->data = (unsigned char *)
+ MapViewOfFile(zf->mountHandle, FILE_MAP_READ, 0, 0, zf->length);
+ if (!zf->data) {
+ ZIPFS_POSIX_ERROR(interp, "file mapping failed");
+ return TCL_ERROR;
+ }
#else /* !_WIN32 */
- zf->length = lseek(PTR2INT(handle), 0, SEEK_END);
- if (zf->length == TCL_IO_FAILURE || zf->length < ZIP_CENTRAL_END_LEN) {
- ZIPFS_POSIX_ERROR(interp, "invalid file size");
- goto error;
- }
- lseek(PTR2INT(handle), 0, SEEK_SET);
- zf->data = (unsigned char *) mmap(0, zf->length, PROT_READ,
- MAP_FILE | MAP_PRIVATE, PTR2INT(handle), 0);
- if (zf->data == MAP_FAILED) {
- ZIPFS_POSIX_ERROR(interp, "file mapping failed");
- goto error;
- }
-#endif /* _WIN32 */
+ int fd = PTR2INT(handle);
+
+ /*
+ * Determine the file size.
+ */
+
+ zf->length = lseek(fd, 0, SEEK_END);
+ if (zf->length == ERROR_LENGTH || zf->length < ZIP_CENTRAL_END_LEN) {
+ ZIPFS_POSIX_ERROR(interp, "invalid file size");
+ return TCL_ERROR;
}
- return ZipFSFindTOC(interp, needZip, zf);
+ lseek(fd, 0, SEEK_SET);
- error:
- ZipFSCloseArchive(interp, zf);
- return TCL_ERROR;
+ zf->data = (unsigned char *)
+ mmap(0, zf->length, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if (zf->data == MAP_FAILED) {
+ ZIPFS_POSIX_ERROR(interp, "file mapping failed");
+ return TCL_ERROR;
+ }
+#endif /* _WIN32 */
+ return TCL_OK;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * IsPasswordValid --
+ *
+ * Basic test for whether a passowrd is valid. If the test fails, sets an
+ * error message in the interpreter.
+ *
+ * Returns:
+ * TCL_OK if the test passes, TCL_ERROR if it fails.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static inline int
+IsPasswordValid(
+ Tcl_Interp *interp,
+ const char *passwd,
+ int pwlen)
+{
+ if ((pwlen > 255) || strchr(passwd, 0xff)) {
+ ZIPFS_ERROR(interp, "illegal password");
+ ZIPFS_ERROR_CODE(interp, "BAD_PASS");
+ return TCL_ERROR;
+ }
+ return TCL_OK;
}
/*
*-------------------------------------------------------------------------
*
- * ZipFSRootNode --
+ * ZipFSCatalogFilesystem --
*
- * This function generates the root node for a ZIPFS filesystem.
+ * This function generates the root node for a ZIPFS filesystem by
+ * reading the ZIP's central directory.
*
* Results:
* TCL_OK on success, TCL_ERROR otherwise with an error message placed
* into the given "interp" if it is not NULL.
*
* Side effects:
- * ...
+ * Will acquire and release the write lock.
*
*-------------------------------------------------------------------------
*/
@@ -1135,7 +1540,7 @@ ZipFSOpenArchive(
static int
ZipFSCatalogFilesystem(
Tcl_Interp *interp, /* Current interpreter. NULLable. */
- ZipFile *zf0,
+ ZipFile *zf0, /* Temporary buffer hold archive descriptors */
const char *mountPoint, /* Mount point path. */
const char *passwd, /* Password for opening the ZIP, or NULL if
* the ZIP is unprotected. */
@@ -1156,16 +1561,22 @@ ZipFSCatalogFilesystem(
pwlen = 0;
if (passwd) {
pwlen = strlen(passwd);
- if ((pwlen > 255) || strchr(passwd, 0xff)) {
- if (interp) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("illegal password", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "BAD_PASS", NULL);
- }
+ if (IsPasswordValid(interp, passwd, pwlen) != TCL_OK) {
return TCL_ERROR;
}
}
+ /*
+ * Validate the TOC data. If that's bad, things fall apart.
+ */
+
+ if (zf0->baseOffset >= zf0->length || zf0->passOffset >= zf0->length ||
+ zf0->directoryOffset >= zf0->length) {
+ ZIPFS_ERROR(interp, "bad zip data");
+ ZIPFS_ERROR_CODE(interp, "BAD_ZIP");
+ return TCL_ERROR;
+ }
+
WriteLock();
/*
@@ -1183,37 +1594,36 @@ ZipFSCatalogFilesystem(
hPtr = Tcl_CreateHashEntry(&ZipFS.zipHash, mountPoint, &isNew);
if (!isNew) {
if (interp) {
- zf = (ZipFile *)Tcl_GetHashValue(hPtr);
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"%s is already mounted on %s", zf->name, mountPoint));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "MOUNTED", NULL);
+ ZIPFS_ERROR_CODE(interp, "MOUNTED");
}
Unlock();
ZipFSCloseArchive(interp, zf0);
return TCL_ERROR;
}
- zf = (ZipFile *)Tcl_AttemptAlloc(sizeof(ZipFile) + strlen(mountPoint) + 1);
+ zf = AllocateZipFile(interp, strlen(mountPoint));
if (!zf) {
- if (interp) {
- Tcl_AppendResult(interp, "out of memory", (char *) NULL);
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
Unlock();
ZipFSCloseArchive(interp, zf0);
return TCL_ERROR;
}
Unlock();
+ /*
+ * Convert to a real archive descriptor.
+ */
+
*zf = *zf0;
- zf->mountPoint = (char *)Tcl_GetHashKey(&ZipFS.zipHash, hPtr);
+ zf->mountPoint = (char *) Tcl_GetHashKey(&ZipFS.zipHash, hPtr);
Tcl_CreateExitHandler(ZipfsExitHandler, zf);
zf->mountPointLen = strlen(zf->mountPoint);
+
zf->nameLength = strlen(zipname);
- zf->name = (char *)Tcl_Alloc(zf->nameLength + 1);
+ zf->name = (char *) Tcl_Alloc(zf->nameLength + 1);
memcpy(zf->name, zipname, zf->nameLength + 1);
- zf->entries = NULL;
- zf->topEnts = NULL;
- zf->numOpen = 0;
+
Tcl_SetHashValue(hPtr, zf);
if ((zf->passBuf[0] == 0) && pwlen) {
int k = 0;
@@ -1228,21 +1638,15 @@ ZipFSCatalogFilesystem(
if (mountPoint[0] != '\0') {
hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, mountPoint, &isNew);
if (isNew) {
- z = (ZipEntry *)Tcl_Alloc(sizeof(ZipEntry));
+ z = AllocateZipEntry();
Tcl_SetHashValue(hPtr, z);
- z->tnext = NULL;
z->depth = CountSlashes(mountPoint);
z->zipFilePtr = zf;
z->isDirectory = (zf->baseOffset == 0) ? 1 : -1; /* root marker */
- z->isEncrypted = 0;
z->offset = zf->baseOffset;
- z->crc32 = 0;
- z->timestamp = 0;
- z->numBytes = z->numCompressedBytes = 0;
z->compressMethod = ZIP_COMPMETH_STORED;
- z->data = NULL;
- z->name = (char *)Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
+ z->name = (char *) Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
z->next = zf->entries;
zf->entries = z;
}
@@ -1250,17 +1654,17 @@ ZipFSCatalogFilesystem(
q = zf->data + zf->directoryOffset;
Tcl_DStringInit(&fpBuf);
for (i = 0; i < zf->numFiles; i++) {
+ const unsigned char *start = zf->data;
+ const unsigned char *end = zf->data + zf->length;
int extra, isdir = 0, dosTime, dosDate, nbcompr;
size_t offs, pathlen, comlen;
unsigned char *lq, *gq = NULL;
char *fullpath, *path;
- pathlen = ZipReadShort(q + ZIP_CENTRAL_PATHLEN_OFFS);
- comlen = ZipReadShort(q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
- extra = ZipReadShort(q + ZIP_CENTRAL_EXTRALEN_OFFS);
- Tcl_DStringSetLength(&ds, 0);
- Tcl_DStringAppend(&ds, (char *) q + ZIP_CENTRAL_HEADER_LEN, pathlen);
- path = Tcl_DStringValue(&ds);
+ pathlen = ZipReadShort(start, end, q + ZIP_CENTRAL_PATHLEN_OFFS);
+ comlen = ZipReadShort(start, end, q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
+ extra = ZipReadShort(start, end, q + ZIP_CENTRAL_EXTRALEN_OFFS);
+ path = DecodeZipEntryText(q + ZIP_CENTRAL_HEADER_LEN, pathlen, &ds);
if ((pathlen > 0) && (path[pathlen - 1] == '/')) {
Tcl_DStringSetLength(&ds, pathlen - 1);
path = Tcl_DStringValue(&ds);
@@ -1270,24 +1674,25 @@ ZipFSCatalogFilesystem(
goto nextent;
}
lq = zf->data + zf->baseOffset
- + ZipReadInt(q + ZIP_CENTRAL_LOCALHDR_OFFS);
- if ((lq < zf->data) || (lq > zf->data + zf->length)) {
+ + ZipReadInt(start, end, q + ZIP_CENTRAL_LOCALHDR_OFFS);
+ if ((lq < start) || (lq + ZIP_LOCAL_HEADER_LEN > end)) {
goto nextent;
}
- nbcompr = ZipReadInt(lq + ZIP_LOCAL_COMPLEN_OFFS);
+ nbcompr = ZipReadInt(start, end, lq + ZIP_LOCAL_COMPLEN_OFFS);
if (!isdir && (nbcompr == 0)
- && (ZipReadInt(lq + ZIP_LOCAL_UNCOMPLEN_OFFS) == 0)
- && (ZipReadInt(lq + ZIP_LOCAL_CRC32_OFFS) == 0)) {
+ && (ZipReadInt(start, end, lq + ZIP_LOCAL_UNCOMPLEN_OFFS) == 0)
+ && (ZipReadInt(start, end, lq + ZIP_LOCAL_CRC32_OFFS) == 0)) {
gq = q;
- nbcompr = ZipReadInt(gq + ZIP_CENTRAL_COMPLEN_OFFS);
+ nbcompr = ZipReadInt(start, end, gq + ZIP_CENTRAL_COMPLEN_OFFS);
}
offs = (lq - zf->data)
+ ZIP_LOCAL_HEADER_LEN
- + ZipReadShort(lq + ZIP_LOCAL_PATHLEN_OFFS)
- + ZipReadShort(lq + ZIP_LOCAL_EXTRALEN_OFFS);
+ + ZipReadShort(start, end, lq + ZIP_LOCAL_PATHLEN_OFFS)
+ + ZipReadShort(start, end, lq + ZIP_LOCAL_EXTRALEN_OFFS);
if (offs + nbcompr > zf->length) {
goto nextent;
}
+
if (!isdir && (mountPoint[0] == '\0') && !CountSlashes(path)) {
#ifdef ANDROID
/*
@@ -1303,8 +1708,7 @@ ZipFSCatalogFilesystem(
Tcl_DStringInit(&ds2);
Tcl_DStringAppend(&ds2, "assets/.root/", -1);
Tcl_DStringAppend(&ds2, path, -1);
- hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, Tcl_DStringValue(&ds2));
- if (hPtr) {
+ if (ZipFSLookup(Tcl_DStringValue(&ds2))) {
/* should not happen but skip it anyway */
Tcl_DStringFree(&ds2);
goto nextent;
@@ -1321,83 +1725,91 @@ ZipFSCatalogFilesystem(
goto nextent;
#endif /* ANDROID */
}
+
Tcl_DStringSetLength(&fpBuf, 0);
fullpath = CanonicalPath(mountPoint, path, &fpBuf, 1);
- z = (ZipEntry *)Tcl_Alloc(sizeof(ZipEntry));
- z->name = NULL;
- z->tnext = NULL;
+ z = AllocateZipEntry();
z->depth = CountSlashes(fullpath);
z->zipFilePtr = zf;
z->isDirectory = isdir;
- z->isEncrypted = (ZipReadShort(lq + ZIP_LOCAL_FLAGS_OFFS) & 1)
+ z->isEncrypted =
+ (ZipReadShort(start, end, lq + ZIP_LOCAL_FLAGS_OFFS) & 1)
&& (nbcompr > 12);
z->offset = offs;
if (gq) {
- z->crc32 = ZipReadInt(gq + ZIP_CENTRAL_CRC32_OFFS);
- dosDate = ZipReadShort(gq + ZIP_CENTRAL_MDATE_OFFS);
- dosTime = ZipReadShort(gq + ZIP_CENTRAL_MTIME_OFFS);
+ z->crc32 = ZipReadInt(start, end, gq + ZIP_CENTRAL_CRC32_OFFS);
+ dosDate = ZipReadShort(start, end, gq + ZIP_CENTRAL_MDATE_OFFS);
+ dosTime = ZipReadShort(start, end, gq + ZIP_CENTRAL_MTIME_OFFS);
z->timestamp = DosTimeDate(dosDate, dosTime);
- z->numBytes = ZipReadInt(gq + ZIP_CENTRAL_UNCOMPLEN_OFFS);
- z->compressMethod = ZipReadShort(gq + ZIP_CENTRAL_COMPMETH_OFFS);
+ z->numBytes = ZipReadInt(start, end,
+ gq + ZIP_CENTRAL_UNCOMPLEN_OFFS);
+ z->compressMethod = ZipReadShort(start, end,
+ gq + ZIP_CENTRAL_COMPMETH_OFFS);
} else {
- z->crc32 = ZipReadInt(lq + ZIP_LOCAL_CRC32_OFFS);
- dosDate = ZipReadShort(lq + ZIP_LOCAL_MDATE_OFFS);
- dosTime = ZipReadShort(lq + ZIP_LOCAL_MTIME_OFFS);
+ z->crc32 = ZipReadInt(start, end, lq + ZIP_LOCAL_CRC32_OFFS);
+ dosDate = ZipReadShort(start, end, lq + ZIP_LOCAL_MDATE_OFFS);
+ dosTime = ZipReadShort(start, end, lq + ZIP_LOCAL_MTIME_OFFS);
z->timestamp = DosTimeDate(dosDate, dosTime);
- z->numBytes = ZipReadInt(lq + ZIP_LOCAL_UNCOMPLEN_OFFS);
- z->compressMethod = ZipReadShort(lq + ZIP_LOCAL_COMPMETH_OFFS);
+ z->numBytes = ZipReadInt(start, end,
+ lq + ZIP_LOCAL_UNCOMPLEN_OFFS);
+ z->compressMethod = ZipReadShort(start, end,
+ lq + ZIP_LOCAL_COMPMETH_OFFS);
}
z->numCompressedBytes = nbcompr;
- z->data = NULL;
hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, fullpath, &isNew);
if (!isNew) {
/* should not happen but skip it anyway */
Tcl_Free(z);
- } else {
- Tcl_SetHashValue(hPtr, z);
- z->name = (char *)Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
- z->next = zf->entries;
- zf->entries = z;
- if (isdir && (mountPoint[0] == '\0') && (z->depth == 1)) {
- z->tnext = zf->topEnts;
- zf->topEnts = z;
- }
- if (!z->isDirectory && (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);
- for (end = strrchr(dir, '/'); end && (end != dir);
- end = strrchr(dir, '/')) {
- Tcl_DStringSetLength(&ds, end - dir);
- hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, dir, &isNew);
- if (!isNew) {
- break;
- }
- zd = (ZipEntry *)Tcl_Alloc(sizeof(ZipEntry));
- zd->name = NULL;
- zd->tnext = NULL;
- zd->depth = CountSlashes(dir);
- zd->zipFilePtr = zf;
- zd->isDirectory = 1;
- zd->isEncrypted = 0;
- zd->offset = z->offset;
- zd->crc32 = 0;
- zd->timestamp = z->timestamp;
- zd->numBytes = zd->numCompressedBytes = 0;
- zd->compressMethod = ZIP_COMPMETH_STORED;
- zd->data = NULL;
- Tcl_SetHashValue(hPtr, zd);
- zd->name = (char *)Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
- zd->next = zf->entries;
- zf->entries = zd;
- if ((mountPoint[0] == '\0') && (zd->depth == 1)) {
- zd->tnext = zf->topEnts;
- zf->topEnts = zd;
- }
+ goto nextent;
+ }
+
+ Tcl_SetHashValue(hPtr, z);
+ z->name = (char *) Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
+ z->next = zf->entries;
+ zf->entries = z;
+ if (isdir && (mountPoint[0] == '\0') && (z->depth == 1)) {
+ z->tnext = zf->topEnts;
+ zf->topEnts = z;
+ }
+
+ /*
+ * Make any directory nodes we need. ZIPs are not consistent about
+ * containing directory nodes.
+ */
+
+ if (!z->isDirectory && (z->depth > 1)) {
+ char *dir, *endPtr;
+ ZipEntry *zd;
+
+ Tcl_DStringSetLength(&ds, strlen(z->name) + 8);
+ Tcl_DStringSetLength(&ds, 0);
+ Tcl_DStringAppend(&ds, z->name, -1);
+ dir = Tcl_DStringValue(&ds);
+ for (endPtr = strrchr(dir, '/'); endPtr && (endPtr != dir);
+ endPtr = strrchr(dir, '/')) {
+ Tcl_DStringSetLength(&ds, endPtr - dir);
+ hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, dir, &isNew);
+ if (!isNew) {
+ /*
+ * Already made. That's fine.
+ */
+ break;
+ }
+
+ zd = AllocateZipEntry();
+ zd->depth = CountSlashes(dir);
+ zd->zipFilePtr = zf;
+ zd->isDirectory = 1;
+ zd->offset = z->offset;
+ zd->timestamp = z->timestamp;
+ zd->compressMethod = ZIP_COMPMETH_STORED;
+ Tcl_SetHashValue(hPtr, zd);
+ zd->name = (char *) Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
+ zd->next = zf->entries;
+ zf->entries = zd;
+ if ((mountPoint[0] == '\0') && (zd->depth == 1)) {
+ zd->tnext = zf->topEnts;
+ zf->topEnts = zd;
}
}
}
@@ -1442,6 +1854,10 @@ ZipfsSetup(void)
Tcl_InitHashTable(&ZipFS.zipHash, TCL_STRING_KEYS);
ZipFS.idCount = 1;
ZipFS.wrmax = DEFAULT_WRITE_MAX_SIZE;
+ ZipFS.fallbackEntryEncoding =
+ Tcl_Alloc(strlen(ZIPFS_FALLBACK_ENCODING) + 1);
+ strcpy(ZipFS.fallbackEntryEncoding, ZIPFS_FALLBACK_ENCODING);
+ ZipFS.utf8 = Tcl_GetEncoding(NULL, "utf-8");
ZipFS.initialized = 1;
}
@@ -1471,17 +1887,28 @@ ListMountPoints(
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
ZipFile *zf;
+ Tcl_Obj *resultList;
+
+ if (!interp) {
+ /*
+ * Are there any entries in the zipHash? Don't need to enumerate them
+ * all to know.
+ */
+
+ return (ZipFS.zipHash.numEntries ? TCL_OK : TCL_BREAK);
+ }
+ resultList = Tcl_NewObj();
for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
hPtr = Tcl_NextHashEntry(&search)) {
- if (!interp) {
- return TCL_OK;
- }
- zf = (ZipFile *)Tcl_GetHashValue(hPtr);
- Tcl_AppendElement(interp, zf->mountPoint);
- Tcl_AppendElement(interp, zf->name);
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
+ Tcl_ListObjAppendElement(NULL, resultList, Tcl_NewStringObj(
+ zf->mountPoint, -1));
+ Tcl_ListObjAppendElement(NULL, resultList, Tcl_NewStringObj(
+ zf->name, -1));
}
- return (interp ? TCL_OK : TCL_BREAK);
+ Tcl_SetObjResult(interp, resultList);
+ return TCL_OK;
}
/*
@@ -1508,13 +1935,10 @@ DescribeMounted(
Tcl_Interp *interp,
const char *mountPoint)
{
- Tcl_HashEntry *hPtr;
- ZipFile *zf;
-
if (interp) {
- hPtr = Tcl_FindHashEntry(&ZipFS.zipHash, mountPoint);
- if (hPtr) {
- zf = (ZipFile *)Tcl_GetHashValue(hPtr);
+ ZipFile *zf = ZipFSLookupZip(mountPoint);
+
+ if (zf) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(zf->name, -1));
return TCL_OK;
}
@@ -1544,7 +1968,8 @@ int
TclZipfs_Mount(
Tcl_Interp *interp, /* Current interpreter. NULLable. */
const char *mountPoint, /* Mount point path. */
- const char *zipname, /* Path to ZIP file to mount. */
+ const char *zipname, /* Path to ZIP file to mount; should be
+ * normalized. */
const char *passwd) /* Password for opening the ZIP, or NULL if
* the ZIP is unprotected. */
{
@@ -1581,22 +2006,11 @@ TclZipfs_Mount(
* Have both a mount point and a file (name) to mount there.
*/
- if (passwd) {
- if ((strlen(passwd) > 255) || strchr(passwd, 0xff)) {
- if (interp) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("illegal password", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "BAD_PASS", NULL);
- }
- return TCL_ERROR;
- }
+ if (passwd && IsPasswordValid(interp, passwd, strlen(passwd)) != TCL_OK) {
+ return TCL_ERROR;
}
- zf = (ZipFile *)Tcl_AttemptAlloc(sizeof(ZipFile) + strlen(mountPoint) + 1);
+ zf = AllocateZipFile(interp, strlen(mountPoint));
if (!zf) {
- if (interp) {
- Tcl_AppendResult(interp, "out of memory", (char *) NULL);
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
return TCL_ERROR;
}
if (ZipFSOpenArchive(interp, zipname, 1, zf) != TCL_OK) {
@@ -1672,23 +2086,16 @@ TclZipfs_MountBuffer(
* Have both a mount point and data to mount there.
*/
- zf = (ZipFile *)Tcl_AttemptAlloc(sizeof(ZipFile) + strlen(mountPoint) + 1);
+ zf = AllocateZipFile(interp, strlen(mountPoint));
if (!zf) {
- if (interp) {
- Tcl_AppendResult(interp, "out of memory", (char *) NULL);
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
return TCL_ERROR;
}
zf->isMemBuffer = 1;
zf->length = datalen;
if (copy) {
- zf->data = (unsigned char *)Tcl_AttemptAlloc(datalen);
+ zf->data = (unsigned char *) Tcl_AttemptAlloc(datalen);
if (!zf->data) {
- if (interp) {
- Tcl_AppendResult(interp, "out of memory", (char *) NULL);
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
+ ZIPFS_MEM_ERROR(interp);
return TCL_ERROR;
}
memcpy(zf->data, data, datalen);
@@ -1697,7 +2104,6 @@ TclZipfs_MountBuffer(
zf->data = data;
zf->ptrToFree = NULL;
}
- zf->passBuf[0] = 0; /* stop valgrind cries */
if (ZipFSFindTOC(interp, 0, zf) != TCL_OK) {
return TCL_ERROR;
}
@@ -1753,13 +2159,20 @@ TclZipfs_Unmount(
goto done;
}
- zf = (ZipFile *)Tcl_GetHashValue(hPtr);
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
if (zf->numOpen > 0) {
ZIPFS_ERROR(interp, "filesystem is busy");
+ ZIPFS_ERROR_CODE(interp, "BUSY");
ret = TCL_ERROR;
goto done;
}
Tcl_DeleteHashEntry(hPtr);
+
+ /*
+ * Now no longer mounted - the rest of the code won't find it - but we're
+ * still cleaning things up.
+ */
+
for (z = zf->entries; z; z = znext) {
znext = z->next;
hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, z->name);
@@ -1775,6 +2188,7 @@ TclZipfs_Unmount(
Tcl_DeleteExitHandler(ZipfsExitHandler, zf);
Tcl_Free(zf);
unmounted = 1;
+
done:
Unlock();
if (unmounted) {
@@ -1806,16 +2220,38 @@ ZipFSMountObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ const char *mountPoint = NULL, *zipFile = NULL, *password = NULL;
+ Tcl_Obj *zipFileObj = NULL;
+ int result;
if (objc > 4) {
Tcl_WrongNumArgs(interp, 1, objv,
"?mountpoint? ?zipfile? ?password?");
return TCL_ERROR;
}
+ if (objc > 1) {
+ mountPoint = TclGetString(objv[1]);
+ }
+ if (objc > 2) {
+ zipFileObj = Tcl_FSGetNormalizedPath(interp, objv[2]);
+ if (!zipFileObj) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "could not normalize zip filename", -1));
+ Tcl_SetErrorCode(interp, "TCL", "OPERATION", "NORMALIZE", NULL);
+ return TCL_ERROR;
+ }
+ Tcl_IncrRefCount(zipFileObj);
+ zipFile = TclGetString(zipFileObj);
+ }
+ if (objc > 3) {
+ password = TclGetString(objv[3]);
+ }
- return TclZipfs_Mount(interp, (objc > 1) ? TclGetString(objv[1]) : NULL,
- (objc > 2) ? TclGetString(objv[2]) : NULL,
- (objc > 3) ? TclGetString(objv[3]) : NULL);
+ result = TclZipfs_Mount(interp, mountPoint, zipFile, password);
+ if (zipFileObj != NULL) {
+ Tcl_DecrRefCount(zipFileObj);
+ }
+ return result;
}
/*
@@ -1843,7 +2279,7 @@ ZipFSMountBufferObjCmd(
{
const char *mountPoint; /* Mount point path. */
unsigned char *data;
- size_t length = 0;
+ size_t length;
if (objc > 3) {
Tcl_WrongNumArgs(interp, 1, objv, "?mountpoint? ?data?");
@@ -1923,7 +2359,6 @@ ZipFSUnmountObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
-
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "zipfile");
return TCL_ERROR;
@@ -1956,49 +2391,95 @@ ZipFSMkKeyObjCmd(
Tcl_Obj *const objv[]) /* Argument objects. */
{
int len, i = 0;
- char *pw, passBuf[264];
+ const char *pw;
+ Tcl_Obj *passObj;
+ unsigned char *passBuf;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "password");
return TCL_ERROR;
}
- pw = TclGetString(objv[1]);
- len = strlen(pw);
+ pw = TclGetStringFromObj(objv[1], &len);
if (len == 0) {
return TCL_OK;
}
- if ((len > 255) || strchr(pw, 0xff)) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj("illegal password", -1));
+ if (IsPasswordValid(interp, pw, len) != TCL_OK) {
return TCL_ERROR;
}
+
+ passObj = Tcl_NewByteArrayObj(NULL, 264);
+ passBuf = Tcl_GetByteArrayFromObj(passObj, (int *)NULL);
while (len > 0) {
int ch = pw[len - 1];
- passBuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
- i++;
+ passBuf[i++] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
len--;
}
passBuf[i] = i;
- ++i;
- passBuf[i++] = (char) ZIP_PASSWORD_END_SIG;
- passBuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 8);
- passBuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 16);
- passBuf[i++] = (char) (ZIP_PASSWORD_END_SIG >> 24);
- passBuf[i] = '\0';
- Tcl_AppendResult(interp, passBuf, (char *) NULL);
+ i++;
+ ZipWriteInt(passBuf, passBuf + 264, passBuf + i, ZIP_PASSWORD_END_SIG);
+ Tcl_SetByteArrayLength(passObj, i + 4);
+ Tcl_SetObjResult(interp, passObj);
return TCL_OK;
}
/*
*-------------------------------------------------------------------------
*
+ * RandomChar --
+ *
+ * Worker for ZipAddFile(). Picks a random character (range: 0..255)
+ * using Tcl's standard PRNG.
+ *
+ * Returns:
+ * Tcl result code. Updates chPtr with random character on success.
+ *
+ * Side effects:
+ * Advances the PRNG state. May reenter the Tcl interpreter if the user
+ * has replaced the PRNG.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int
+RandomChar(
+ Tcl_Interp *interp,
+ int step,
+ int *chPtr)
+{
+ double r;
+ Tcl_Obj *ret;
+
+ if (Tcl_EvalEx(interp, "::tcl::mathfunc::rand", -1, 0) != TCL_OK) {
+ goto failed;
+ }
+ ret = Tcl_GetObjResult(interp);
+ if (Tcl_GetDoubleFromObj(interp, ret, &r) != TCL_OK) {
+ goto failed;
+ }
+ *chPtr = (int) (r * 256);
+ return TCL_OK;
+
+ failed:
+ Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
+ "\n (evaluating PRNG step %d for password encoding)",
+ step));
+ return TCL_ERROR;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
* ZipAddFile --
*
- * This procedure is used by ZipFSMkZipOrImgCmd() to add a single file to
+ * This procedure is used by ZipFSMkZipOrImg() to add a single file to
* the output ZIP archive file being written. A ZipEntry struct about the
* input file is added to the given fileHash table for later creation of
* the central ZIP directory.
*
+ * Tcl *always* encodes filenames in the ZIP as UTF-8. Similarly, it
+ * would always encode comments as UTF-8, if it supported comments.
+ *
* Results:
* A standard Tcl result.
*
@@ -2012,23 +2493,30 @@ ZipFSMkKeyObjCmd(
static int
ZipAddFile(
Tcl_Interp *interp, /* Current interpreter. */
- const char *path,
- const char *name,
- Tcl_Channel out,
+ Tcl_Obj *pathObj, /* Actual name of the file to add. */
+ const char *name, /* Name to use in the ZIP archive, in Tcl's
+ * internal encoding. */
+ Tcl_Channel out, /* The open ZIP archive being built. */
const char *passwd, /* Password for encoding the file, or NULL if
* the file is to be unprotected. */
- char *buf,
- int bufsize,
- Tcl_HashTable *fileHash)
+ char *buf, /* Working buffer. */
+ int bufsize, /* Size of buf */
+ Tcl_HashTable *fileHash) /* Where to record ZIP entry metdata so we can
+ * built the central directory. */
{
+ const unsigned char *start = (unsigned char *) buf;
+ const unsigned char *end = (unsigned char *) buf + bufsize;
Tcl_Channel in;
Tcl_HashEntry *hPtr;
ZipEntry *z;
z_stream stream;
- const char *zpath;
+ Tcl_DString zpathDs; /* Buffer for the encoded filename. */
+ const char *zpathExt; /* Filename in external encoding (true
+ * UTF-8). */
+ const char *zpathTcl; /* Filename in Tcl's internal encoding. */
int crc, flush, zpathlen;
size_t nbyte, nbytecompr, len, olen, align = 0;
- long long pos[3];
+ long long headerStartOffset, dataStartOffset, dataEndOffset;
int mtime = 0, isNew, compMeth;
unsigned long keys[3], keys0[3];
char obuf[4096];
@@ -2038,55 +2526,67 @@ ZipAddFile(
* nothing to do.
*/
- zpath = name;
- while (zpath && zpath[0] == '/') {
- zpath++;
+ zpathTcl = name;
+ while (zpathTcl && zpathTcl[0] == '/') {
+ zpathTcl++;
}
- if (!zpath || (zpath[0] == '\0')) {
+ if (!zpathTcl || (zpathTcl[0] == '\0')) {
return TCL_OK;
}
- zpathlen = strlen(zpath);
+ /*
+ * Convert to encoded form. Note that we use strlen() here; if someone's
+ * crazy enough to embed NULs in filenames, they deserve what they get!
+ */
+
+ zpathExt = Tcl_UtfToExternalDString(ZipFS.utf8, zpathTcl, -1, &zpathDs);
+ zpathlen = strlen(zpathExt);
if (zpathlen + ZIP_CENTRAL_HEADER_LEN > bufsize) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "path too long for \"%s\"", path));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "PATH_LEN", NULL);
+ "path too long for \"%s\"", TclGetString(pathObj)));
+ ZIPFS_ERROR_CODE(interp, "PATH_LEN");
+ Tcl_DStringFree(&zpathDs);
return TCL_ERROR;
}
- in = Tcl_OpenFileChannel(interp, path, "rb", 0);
+ in = Tcl_FSOpenFileChannel(interp, pathObj, "rb", 0);
if (!in) {
+ Tcl_DStringFree(&zpathDs);
#ifdef _WIN32
/* hopefully a directory */
if (strcmp("permission denied", Tcl_PosixError(interp)) == 0) {
- Tcl_CloseEx(interp, in, 0);
+ Tcl_Close(interp, in);
return TCL_OK;
}
#endif /* _WIN32 */
- Tcl_CloseEx(interp, in, 0);
+ 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);
+
+ /*
+ * Compute the CRC.
+ */
+
crc = 0;
nbyte = nbytecompr = 0;
while (1) {
len = Tcl_Read(in, buf, bufsize);
- if (len == TCL_IO_FAILURE) {
+ if (len == ERROR_LENGTH) {
+ Tcl_DStringFree(&zpathDs);
if (nbyte == 0 && errno == EISDIR) {
- Tcl_CloseEx(interp, in, 0);
+ Tcl_Close(interp, in);
return TCL_OK;
}
+ readErrorWithChannelOpen:
Tcl_SetObjResult(interp, Tcl_ObjPrintf("read error on \"%s\": %s",
- path, Tcl_PosixError(interp)));
- Tcl_CloseEx(interp, in, 0);
+ TclGetString(pathObj), Tcl_PosixError(interp)));
+ Tcl_Close(interp, in);
return TCL_ERROR;
}
if (len == 0) {
@@ -2097,66 +2597,70 @@ ZipAddFile(
}
if (Tcl_Seek(in, 0, SEEK_SET) == -1) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("seek error on \"%s\": %s",
- path, Tcl_PosixError(interp)));
- Tcl_CloseEx(interp, in, 0);
+ TclGetString(pathObj), Tcl_PosixError(interp)));
+ Tcl_Close(interp, in);
+ Tcl_DStringFree(&zpathDs);
return TCL_ERROR;
}
- pos[0] = Tcl_Tell(out);
+
+ /*
+ * Remember where we've got to so far so we can write the header (after
+ * writing the file).
+ */
+
+ headerStartOffset = Tcl_Tell(out);
+
+ /*
+ * Reserve space for the per-file header. Includes writing the file name
+ * as we already know that.
+ */
+
memset(buf, '\0', ZIP_LOCAL_HEADER_LEN);
- memcpy(buf + ZIP_LOCAL_HEADER_LEN, zpath, zpathlen);
+ memcpy(buf + ZIP_LOCAL_HEADER_LEN, zpathExt, zpathlen);
len = zpathlen + ZIP_LOCAL_HEADER_LEN;
- if (Tcl_Write(out, buf, len) != len) {
- wrerr:
+ if ((size_t) Tcl_Write(out, buf, len) != len) {
+ writeErrorWithChannelOpen:
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error on %s: %s", path, Tcl_PosixError(interp)));
- Tcl_CloseEx(interp, in, 0);
+ "write error on \"%s\": %s",
+ TclGetString(pathObj), Tcl_PosixError(interp)));
+ Tcl_Close(interp, in);
+ Tcl_DStringFree(&zpathDs);
return TCL_ERROR;
}
- if ((len + pos[0]) & 3) {
- unsigned 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 payload to next 4-byte boundary (if necessary) using a dummy
+ * extra entry similar to the zipalign tool from Android's SDK.
+ */
- align = 4 + ((len + pos[0]) & 3);
- ZipWriteShort(abuf, 0xffff);
- ZipWriteShort(abuf + 2, align - 4);
- ZipWriteInt(abuf + 4, 0x03020100);
- if (Tcl_Write(out, (const char *) abuf, align) != align) {
- goto wrerr;
+ if ((len + headerStartOffset) & 3) {
+ unsigned char abuf[8];
+ const unsigned char *astart = abuf;
+ const unsigned char *aend = abuf + 8;
+
+ align = 4 + ((len + headerStartOffset) & 3);
+ ZipWriteShort(astart, aend, abuf, 0xffff);
+ ZipWriteShort(astart, aend, abuf + 2, align - 4);
+ ZipWriteInt(astart, aend, abuf + 4, 0x03020100);
+ if ((size_t) Tcl_Write(out, (const char *) abuf, align) != align) {
+ goto writeErrorWithChannelOpen;
}
}
+
+ /*
+ * Set up encryption if we were asked to.
+ */
+
if (passwd) {
int i, ch, tmp;
unsigned char kvbuf[24];
- Tcl_Obj *ret;
init_keys(passwd, keys, crc32tab);
for (i = 0; i < 12 - 2; i++) {
- double r;
-
- if (Tcl_EvalEx(interp, "::tcl::mathfunc::rand", -1, 0) != TCL_OK) {
- Tcl_Obj *eiPtr = Tcl_ObjPrintf(
- "\n (evaluating PRNG step %d for password encoding)",
- i);
-
- Tcl_AppendObjToErrorInfo(interp, eiPtr);
- Tcl_CloseEx(interp, in, 0);
- return TCL_ERROR;
- }
- ret = Tcl_GetObjResult(interp);
- if (Tcl_GetDoubleFromObj(interp, ret, &r) != TCL_OK) {
- Tcl_Obj *eiPtr = Tcl_ObjPrintf(
- "\n (evaluating PRNG step %d for password encoding)",
- i);
-
- Tcl_AppendObjToErrorInfo(interp, eiPtr);
- Tcl_CloseEx(interp, in, 0);
+ if (RandomChar(interp, i, &ch) != TCL_OK) {
+ Tcl_Close(interp, in);
return TCL_ERROR;
}
- ch = (int) (r * 256);
kvbuf[i + 12] = UCHAR(zencode(keys, crc32tab, ch, tmp));
}
Tcl_ResetResult(interp);
@@ -2169,16 +2673,23 @@ ZipAddFile(
len = Tcl_Write(out, (char *) kvbuf, 12);
memset(kvbuf, 0, 24);
if (len != 12) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error on %s: %s", path, Tcl_PosixError(interp)));
- Tcl_CloseEx(interp, in, 0);
- return TCL_ERROR;
+ goto writeErrorWithChannelOpen;
}
memcpy(keys0, keys, sizeof(keys0));
nbytecompr += 12;
}
+
+ /*
+ * Save where we've got to in case we need to just store this file.
+ */
+
Tcl_Flush(out);
- pos[2] = Tcl_Tell(out);
+ dataStartOffset = Tcl_Tell(out);
+
+ /*
+ * Compress the stream.
+ */
+
compMeth = ZIP_COMPMETH_DEFLATED;
memset(&stream, 0, sizeof(z_stream));
stream.zalloc = Z_NULL;
@@ -2187,19 +2698,18 @@ ZipAddFile(
if (deflateInit2(&stream, 9, Z_DEFLATED, -15, 8,
Z_DEFAULT_STRATEGY) != Z_OK) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "compression init error on \"%s\"", path));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "DEFLATE_INIT", NULL);
- Tcl_CloseEx(interp, in, 0);
+ "compression init error on \"%s\"", TclGetString(pathObj)));
+ ZIPFS_ERROR_CODE(interp, "DEFLATE_INIT");
+ Tcl_Close(interp, in);
+ Tcl_DStringFree(&zpathDs);
return TCL_ERROR;
}
+
do {
len = Tcl_Read(in, buf, bufsize);
- if (len == TCL_IO_FAILURE) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "read error on %s: %s", path, Tcl_PosixError(interp)));
+ if (len == ERROR_LENGTH) {
deflateEnd(&stream);
- Tcl_CloseEx(interp, in, 0);
- return TCL_ERROR;
+ goto readErrorWithChannelOpen;
}
stream.avail_in = len;
stream.next_in = (unsigned char *) buf;
@@ -2210,10 +2720,11 @@ ZipAddFile(
len = deflate(&stream, flush);
if (len == (size_t) Z_STREAM_ERROR) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "deflate error on %s", path));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "DEFLATE", NULL);
+ "deflate error on \"%s\"", TclGetString(pathObj)));
+ ZIPFS_ERROR_CODE(interp, "DEFLATE");
deflateEnd(&stream);
- Tcl_CloseEx(interp, in, 0);
+ Tcl_Close(interp, in);
+ Tcl_DStringFree(&zpathDs);
return TCL_ERROR;
}
olen = sizeof(obuf) - stream.avail_out;
@@ -2225,42 +2736,43 @@ ZipAddFile(
obuf[i] = (char) zencode(keys, crc32tab, obuf[i], tmp);
}
}
- if (olen && (Tcl_Write(out, obuf, olen) != olen)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error: %s", Tcl_PosixError(interp)));
+ if (olen && ((size_t) Tcl_Write(out, obuf, olen) != olen)) {
deflateEnd(&stream);
- Tcl_CloseEx(interp, in, 0);
- return TCL_ERROR;
+ goto writeErrorWithChannelOpen;
}
nbytecompr += olen;
} while (stream.avail_out == 0);
} while (flush != Z_FINISH);
deflateEnd(&stream);
+
+ /*
+ * Work out where we've got to.
+ */
+
Tcl_Flush(out);
- pos[1] = Tcl_Tell(out);
+ dataEndOffset = Tcl_Tell(out);
+
if (nbyte - nbytecompr <= 0) {
/*
* Compressed file larger than input, write it again uncompressed.
*/
+
if (Tcl_Seek(in, 0, SEEK_SET) != 0) {
goto seekErr;
}
- if (Tcl_Seek(out, pos[2], SEEK_SET) != pos[2]) {
+ if (Tcl_Seek(out, dataStartOffset, SEEK_SET) != dataStartOffset) {
seekErr:
- Tcl_CloseEx(interp, in, 0);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"seek error: %s", Tcl_PosixError(interp)));
+ Tcl_Close(interp, in);
+ Tcl_DStringFree(&zpathDs);
return TCL_ERROR;
}
nbytecompr = (passwd ? 12 : 0);
while (1) {
len = Tcl_Read(in, buf, bufsize);
- if (len == TCL_IO_FAILURE) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "read error on \"%s\": %s",
- path, Tcl_PosixError(interp)));
- Tcl_CloseEx(interp, in, 0);
- return TCL_ERROR;
+ if (len == ERROR_LENGTH) {
+ goto readErrorWithChannelOpen;
} else if (len == 0) {
break;
}
@@ -2272,62 +2784,58 @@ ZipAddFile(
buf[i] = (char) zencode(keys0, crc32tab, buf[i], tmp);
}
}
- if (Tcl_Write(out, buf, len) != len) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error: %s", Tcl_PosixError(interp)));
- Tcl_CloseEx(interp, in, 0);
- return TCL_ERROR;
+ if ((size_t) Tcl_Write(out, buf, len) != len) {
+ goto writeErrorWithChannelOpen;
}
nbytecompr += len;
}
compMeth = ZIP_COMPMETH_STORED;
+
+ /*
+ * Chop off everything after this; it's the over-large compressed data
+ * and we don't know if it is going to get overwritten otherwise.
+ */
+
Tcl_Flush(out);
- pos[1] = Tcl_Tell(out);
- Tcl_TruncateChannel(out, pos[1]);
+ dataEndOffset = Tcl_Tell(out);
+ Tcl_TruncateChannel(out, dataEndOffset);
}
- Tcl_CloseEx(interp, in, 0);
+ Tcl_Close(interp, in);
+ Tcl_DStringFree(&zpathDs);
+ zpathExt = NULL;
- hPtr = Tcl_CreateHashEntry(fileHash, zpath, &isNew);
+ hPtr = Tcl_CreateHashEntry(fileHash, zpathTcl, &isNew);
if (!isNew) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "non-unique path name \"%s\"", path));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "DUPLICATE_PATH", NULL);
+ "non-unique path name \"%s\"", TclGetString(pathObj)));
+ ZIPFS_ERROR_CODE(interp, "DUPLICATE_PATH");
return TCL_ERROR;
}
- z = (ZipEntry *)Tcl_Alloc(sizeof(ZipEntry));
+ /*
+ * Remember that we've written the file (for central directory generation)
+ * and generate the local (per-file) header in the space that we reserved
+ * earlier.
+ */
+
+ z = AllocateZipEntry();
Tcl_SetHashValue(hPtr, z);
- z->name = NULL;
- z->tnext = NULL;
- z->depth = 0;
- z->zipFilePtr = NULL;
- z->isDirectory = 0;
z->isEncrypted = (passwd ? 1 : 0);
- z->offset = pos[0];
+ z->offset = headerStartOffset;
z->crc32 = crc;
z->timestamp = mtime;
z->numBytes = nbyte;
z->numCompressedBytes = nbytecompr;
z->compressMethod = compMeth;
- z->data = NULL;
- z->name = (char *)Tcl_GetHashKey(fileHash, hPtr);
- z->next = NULL;
+ z->name = (char *) Tcl_GetHashKey(fileHash, hPtr);
/*
* Write final local header information.
*/
- ZipWriteInt(buf + ZIP_LOCAL_SIG_OFFS, ZIP_LOCAL_HEADER_SIG);
- ZipWriteShort(buf + ZIP_LOCAL_VERSION_OFFS, ZIP_MIN_VERSION);
- ZipWriteShort(buf + ZIP_LOCAL_FLAGS_OFFS, z->isEncrypted);
- ZipWriteShort(buf + ZIP_LOCAL_COMPMETH_OFFS, z->compressMethod);
- ZipWriteShort(buf + ZIP_LOCAL_MTIME_OFFS, ToDosTime(z->timestamp));
- ZipWriteShort(buf + ZIP_LOCAL_MDATE_OFFS, ToDosDate(z->timestamp));
- ZipWriteInt(buf + ZIP_LOCAL_CRC32_OFFS, z->crc32);
- ZipWriteInt(buf + ZIP_LOCAL_COMPLEN_OFFS, z->numCompressedBytes);
- ZipWriteInt(buf + ZIP_LOCAL_UNCOMPLEN_OFFS, z->numBytes);
- ZipWriteShort(buf + ZIP_LOCAL_PATHLEN_OFFS, zpathlen);
- ZipWriteShort(buf + ZIP_LOCAL_EXTRALEN_OFFS, align);
- if (Tcl_Seek(out, pos[0], SEEK_SET) != pos[0]) {
+
+ SerializeLocalEntryHeader(start, end, (unsigned char *) buf, z,
+ zpathlen, align);
+ if (Tcl_Seek(out, headerStartOffset, SEEK_SET) != headerStartOffset) {
Tcl_DeleteHashEntry(hPtr);
Tcl_Free(z);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -2342,7 +2850,7 @@ ZipAddFile(
return TCL_ERROR;
}
Tcl_Flush(out);
- if (Tcl_Seek(out, pos[1], SEEK_SET) != pos[1]) {
+ if (Tcl_Seek(out, dataEndOffset, SEEK_SET) != dataEndOffset) {
Tcl_DeleteHashEntry(hPtr);
Tcl_Free(z);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -2355,12 +2863,101 @@ ZipAddFile(
/*
*-------------------------------------------------------------------------
*
- * ZipFSMkZipOrImgObjCmd --
+ * ZipFSFind --
+ *
+ * Worker for ZipFSMkZipOrImg() that discovers the list of files to add.
+ * Simple wrapper around [zipfs find].
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+ZipFSFind(
+ Tcl_Interp *interp,
+ Tcl_Obj *dirRoot)
+{
+ Tcl_Obj *cmd[2];
+ int result;
+
+ cmd[0] = Tcl_NewStringObj("::tcl::zipfs::find", -1);
+ cmd[1] = dirRoot;
+ Tcl_IncrRefCount(cmd[0]);
+ result = Tcl_EvalObjv(interp, 2, cmd, 0);
+ Tcl_DecrRefCount(cmd[0]);
+ if (result != TCL_OK) {
+ return NULL;
+ }
+ return Tcl_GetObjResult(interp);
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * ComputeNameInArchive --
+ *
+ * Helper for ZipFSMkZipOrImg() that computes what the actual name of a
+ * file in the ZIP archive should be, stripping a prefix (if appropriate)
+ * and any leading slashes. If the result is an empty string, the entry
+ * should be skipped.
+ *
+ * Returns:
+ * Pointer to the name (in Tcl's internal encoding), which will be in
+ * memory owned by one of the argument objects.
+ *
+ * Side effects:
+ * None (if Tcl_Objs have string representations)
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static inline const char *
+ComputeNameInArchive(
+ Tcl_Obj *pathObj, /* The path to the origin file */
+ Tcl_Obj *directNameObj, /* User-specified name for use in the ZIP
+ * archive */
+ const char *strip, /* A prefix to strip; may be NULL if no
+ * stripping need be done. */
+ int slen) /* The length of the prefix; must be 0 if no
+ * stripping need be done. */
+{
+ const char *name;
+ int len;
+
+ if (directNameObj) {
+ name = TclGetString(directNameObj);
+ } else {
+ name = TclGetStringFromObj(pathObj, &len);
+ if (slen > 0) {
+ if ((len <= slen) || (strncmp(strip, name, slen) != 0)) {
+ /*
+ * Guaranteed to be a NUL at the end, which will make this
+ * entry be skipped.
+ */
+
+ return name + len;
+ }
+ name += slen;
+ }
+ }
+ while (name[0] == '/') {
+ ++name;
+ }
+ return name;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * ZipFSMkZipOrImg --
*
* This procedure is creates a new ZIP archive file or image file given
* output filename, input directory of files to be archived, optional
* password, and optional image to be prepended to the output ZIP archive
- * file.
+ * file. It's the core of the implementation of [zipfs mkzip], [zipfs
+ * mkimg], [zipfs lmkzip] and [zipfs lmkimg].
+ *
+ * Tcl *always* encodes filenames in the ZIP as UTF-8. Similarly, it
+ * would always encode comments as UTF-8, if it supported comments.
*
* Results:
* A standard Tcl result.
@@ -2372,95 +2969,103 @@ ZipAddFile(
*/
static int
-ZipFSMkZipOrImgObjCmd(
+ZipFSMkZipOrImg(
Tcl_Interp *interp, /* Current interpreter. */
- int isImg,
- int isList,
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
+ int isImg, /* Are we making an image? */
+ Tcl_Obj *targetFile, /* What file are we making? */
+ Tcl_Obj *dirRoot, /* What directory do we take files from? Do
+ * not specify at the same time as
+ * mappingList (one must be NULL). */
+ Tcl_Obj *mappingList, /* What files are we putting in, and with what
+ * names? Do not specify at the same time as
+ * dirRoot (one must be NULL). */
+ Tcl_Obj *originFile, /* If we're making an image, what file does
+ * the non-ZIP part of the image come from? */
+ Tcl_Obj *stripPrefix, /* Are we going to strip a prefix from
+ * filenames found beneath dirRoot? If NULL,
+ * do not strip anything (except for dirRoot
+ * itself). */
+ Tcl_Obj *passwordObj) /* The password for encoding things. NULL if
+ * there's no password protection. */
{
Tcl_Channel out;
- int pwlen = 0, count, ret = TCL_ERROR, lobjc;
- size_t len, slen = 0, i = 0;
- long long pos[3];
- Tcl_Obj **lobjv, *list = NULL;
+ int pwlen = 0, slen = 0, count, ret = TCL_ERROR, lobjc;
+ size_t len, i = 0;
+ long long dataStartOffset; /* The overall file offset of the start of the
+ * data section of the file. */
+ long long directoryStartOffset;
+ /* The overall file offset of the start of the
+ * central directory. */
+ long long suffixStartOffset;/* The overall file offset of the start of the
+ * suffix of the central directory (i.e.,
+ * where this data will be written). */
+ Tcl_Obj **lobjv, *list = mappingList;
ZipEntry *z;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
Tcl_HashTable fileHash;
char *strip = NULL, *pw = NULL, passBuf[264], buf[4096];
+ unsigned char *start = (unsigned char *) buf;
+ unsigned char *end = start + sizeof(buf);
/*
* Caller has verified that the number of arguments is correct.
*/
passBuf[0] = 0;
- if (objc > (isList ? 3 : 4)) {
- pw = TclGetString(objv[isList ? 3 : 4]);
- pwlen = strlen(pw);
- if ((pwlen > 255) || strchr(pw, 0xff)) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("illegal password", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "BAD_PASS", NULL);
+ if (passwordObj != NULL) {
+ pw = TclGetStringFromObj(passwordObj, &pwlen);
+ if (IsPasswordValid(interp, pw, pwlen) != TCL_OK) {
return TCL_ERROR;
}
+ if (pwlen <= 0) {
+ pw = NULL;
+ pwlen = 0;
+ }
}
- if (isList) {
- list = objv[2];
- Tcl_IncrRefCount(list);
- } else {
- Tcl_Obj *cmd[3];
-
- cmd[1] = Tcl_NewStringObj("::tcl::zipfs::find", -1);
- cmd[2] = objv[2];
- cmd[0] = Tcl_NewListObj(2, cmd + 1);
- Tcl_IncrRefCount(cmd[0]);
- if (Tcl_EvalObjEx(interp, cmd[0], TCL_EVAL_DIRECT) != TCL_OK) {
- Tcl_DecrRefCount(cmd[0]);
+ if (dirRoot != NULL) {
+ list = ZipFSFind(interp, dirRoot);
+ if (!list) {
return TCL_ERROR;
}
- Tcl_DecrRefCount(cmd[0]);
- list = Tcl_GetObjResult(interp);
- Tcl_IncrRefCount(list);
}
+ Tcl_IncrRefCount(list);
if (Tcl_ListObjGetElements(interp, list, &lobjc, &lobjv) != TCL_OK) {
Tcl_DecrRefCount(list);
return TCL_ERROR;
}
- if (isList && (lobjc % 2)) {
+ if (mappingList && (lobjc % 2)) {
Tcl_DecrRefCount(list);
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("need even number of elements", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "LIST_LENGTH", NULL);
+ ZIPFS_ERROR(interp, "need even number of elements");
+ ZIPFS_ERROR_CODE(interp, "LIST_LENGTH");
return TCL_ERROR;
}
if (lobjc == 0) {
Tcl_DecrRefCount(list);
- Tcl_SetObjResult(interp, Tcl_NewStringObj("empty archive", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "EMPTY", NULL);
+ ZIPFS_ERROR(interp, "empty archive");
+ ZIPFS_ERROR_CODE(interp, "EMPTY");
return TCL_ERROR;
}
- out = Tcl_OpenFileChannel(interp, TclGetString(objv[1]), "wb", 0755);
+ out = Tcl_FSOpenFileChannel(interp, targetFile, "wb", 0755);
if (out == NULL) {
Tcl_DecrRefCount(list);
return TCL_ERROR;
}
- if (pwlen <= 0) {
- pw = NULL;
- pwlen = 0;
- }
+
+ /*
+ * Copy the existing contents from the image if it is an executable image.
+ * Care must be taken because this might include an existing ZIP, which
+ * needs to be stripped.
+ */
+
if (isImg) {
ZipFile *zf, zf0;
int isMounted = 0;
const char *imgName;
- if (isList) {
- imgName = (objc > 4) ? TclGetString(objv[4]) :
- Tcl_GetNameOfExecutable();
- } else {
- imgName = (objc > 5) ? TclGetString(objv[5]) :
- Tcl_GetNameOfExecutable();
- }
+ // TODO: normalize the origin file name
+ imgName = (originFile != NULL) ? TclGetString(originFile) :
+ Tcl_GetNameOfExecutable();
if (pwlen) {
i = 0;
for (len = pwlen; len-- > 0;) {
@@ -2485,7 +3090,7 @@ ZipFSMkZipOrImgObjCmd(
WriteLock();
for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
hPtr = Tcl_NextHashEntry(&search)) {
- zf = (ZipFile *)Tcl_GetHashValue(hPtr);
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
if (strcmp(zf->name, imgName) == 0) {
isMounted = 1;
zf->numOpen++;
@@ -2493,17 +3098,22 @@ ZipFSMkZipOrImgObjCmd(
}
}
Unlock();
+
if (!isMounted) {
zf = &zf0;
}
if (isMounted || ZipFSOpenArchive(interp, imgName, 0, zf) == TCL_OK) {
- if (Tcl_Write(out, (char *) zf->data,
+ /*
+ * Copy everything up to the ZIP-related suffix.
+ */
+
+ if ((size_t) Tcl_Write(out, (char *) zf->data,
zf->passOffset) != zf->passOffset) {
memset(passBuf, 0, sizeof(passBuf));
Tcl_DecrRefCount(list);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"write error: %s", Tcl_PosixError(interp)));
- Tcl_CloseEx(interp, out, 0);
+ Tcl_Close(interp, out);
if (zf == &zf0) {
ZipFSCloseArchive(interp, zf);
} else {
@@ -2521,56 +3131,23 @@ ZipFSMkZipOrImgObjCmd(
Unlock();
}
} else {
- size_t k;
- int m, n;
- Tcl_Channel in;
- const char *errMsg = "seek error";
-
/*
* Fall back to read it as plain file which hopefully is a static
* tclsh or wish binary with proper zipfs infrastructure built in.
*/
- Tcl_ResetResult(interp);
- in = Tcl_OpenFileChannel(interp, imgName, "rb", 0644);
- if (!in) {
+ if (CopyImageFile(interp, imgName, out) != TCL_OK) {
memset(passBuf, 0, sizeof(passBuf));
Tcl_DecrRefCount(list);
- Tcl_CloseEx(interp, out, 0);
+ Tcl_Close(interp, out);
return TCL_ERROR;
}
- i = Tcl_Seek(in, 0, SEEK_END);
- if (i == TCL_IO_FAILURE) {
- cperr:
- memset(passBuf, 0, sizeof(passBuf));
- Tcl_DecrRefCount(list);
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "%s: %s", errMsg, Tcl_PosixError(interp)));
- Tcl_CloseEx(interp, out, 0);
- Tcl_CloseEx(interp, in, 0);
- return TCL_ERROR;
- }
- Tcl_Seek(in, 0, SEEK_SET);
- for (k = 0; k < i; k += m) {
- m = i - k;
- if (m > (int) sizeof(buf)) {
- m = (int) sizeof(buf);
- }
- n = Tcl_Read(in, buf, m);
- if (n == -1) {
- errMsg = "read error";
- goto cperr;
- } else if (n == 0) {
- break;
- }
- m = Tcl_Write(out, buf, n);
- if (m != n) {
- errMsg = "write error";
- goto cperr;
- }
- }
- Tcl_CloseEx(interp, in, 0);
}
+
+ /*
+ * Store the password so that the automounter can find it.
+ */
+
len = strlen(passBuf);
if (len > 0) {
i = Tcl_Write(out, passBuf, len);
@@ -2578,112 +3155,81 @@ ZipFSMkZipOrImgObjCmd(
Tcl_DecrRefCount(list);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"write error: %s", Tcl_PosixError(interp)));
- Tcl_CloseEx(interp, out, 0);
+ Tcl_Close(interp, out);
return TCL_ERROR;
}
}
memset(passBuf, 0, sizeof(passBuf));
Tcl_Flush(out);
}
+
+ /*
+ * Prepare the contents of the ZIP archive.
+ */
+
Tcl_InitHashTable(&fileHash, TCL_STRING_KEYS);
- pos[0] = Tcl_Tell(out);
- if (!isList && (objc > 3)) {
- strip = TclGetString(objv[3]);
- slen = strlen(strip);
+ dataStartOffset = Tcl_Tell(out);
+ if (mappingList == NULL && stripPrefix != NULL) {
+ strip = TclGetStringFromObj(stripPrefix, &slen);
+ if (!slen) {
+ strip = NULL;
+ }
}
- for (i = 0; i < (size_t) lobjc; i += (isList ? 2 : 1)) {
- const char *path, *name;
+ for (i = 0; i < (size_t) lobjc; i += (mappingList ? 2 : 1)) {
+ Tcl_Obj *pathObj = lobjv[i];
+ const char *name = ComputeNameInArchive(pathObj,
+ (mappingList ? lobjv[i + 1] : NULL), strip, slen);
- path = TclGetString(lobjv[i]);
- if (isList) {
- name = TclGetString(lobjv[i + 1]);
- } else {
- name = path;
- if (slen > 0) {
- len = strlen(name);
- if ((len <= slen) || (strncmp(strip, name, slen) != 0)) {
- continue;
- }
- name += slen;
- }
- }
- while (name[0] == '/') {
- ++name;
- }
if (name[0] == '\0') {
continue;
}
- if (ZipAddFile(interp, path, name, out, pw, buf, sizeof(buf),
+ if (ZipAddFile(interp, pathObj, name, out, pw, buf, sizeof(buf),
&fileHash) != TCL_OK) {
goto done;
}
}
- pos[1] = Tcl_Tell(out);
+
+ /*
+ * Construct the contents of the ZIP central directory.
+ */
+
+ directoryStartOffset = Tcl_Tell(out);
count = 0;
- for (i = 0; i < (size_t) lobjc; i += (isList ? 2 : 1)) {
- const char *path, *name;
+ for (i = 0; i < (size_t) lobjc; i += (mappingList ? 2 : 1)) {
+ const char *name = ComputeNameInArchive(lobjv[i],
+ (mappingList ? lobjv[i + 1] : NULL), strip, slen);
+ Tcl_DString ds;
- path = TclGetString(lobjv[i]);
- if (isList) {
- name = TclGetString(lobjv[i + 1]);
- } else {
- name = path;
- if (slen > 0) {
- len = strlen(name);
- if ((len <= slen) || (strncmp(strip, name, slen) != 0)) {
- continue;
- }
- name += slen;
- }
- }
- while (name[0] == '/') {
- ++name;
- }
- if (name[0] == '\0') {
- continue;
- }
hPtr = Tcl_FindHashEntry(&fileHash, name);
if (!hPtr) {
continue;
}
- z = (ZipEntry *)Tcl_GetHashValue(hPtr);
- len = strlen(z->name);
- ZipWriteInt(buf + ZIP_CENTRAL_SIG_OFFS, ZIP_CENTRAL_HEADER_SIG);
- ZipWriteShort(buf + ZIP_CENTRAL_VERSIONMADE_OFFS, ZIP_MIN_VERSION);
- ZipWriteShort(buf + ZIP_CENTRAL_VERSION_OFFS, ZIP_MIN_VERSION);
- ZipWriteShort(buf + ZIP_CENTRAL_FLAGS_OFFS, z->isEncrypted);
- ZipWriteShort(buf + ZIP_CENTRAL_COMPMETH_OFFS, z->compressMethod);
- ZipWriteShort(buf + ZIP_CENTRAL_MTIME_OFFS, ToDosTime(z->timestamp));
- ZipWriteShort(buf + ZIP_CENTRAL_MDATE_OFFS, ToDosDate(z->timestamp));
- ZipWriteInt(buf + ZIP_CENTRAL_CRC32_OFFS, z->crc32);
- ZipWriteInt(buf + ZIP_CENTRAL_COMPLEN_OFFS, z->numCompressedBytes);
- ZipWriteInt(buf + ZIP_CENTRAL_UNCOMPLEN_OFFS, z->numBytes);
- ZipWriteShort(buf + ZIP_CENTRAL_PATHLEN_OFFS, len);
- ZipWriteShort(buf + ZIP_CENTRAL_EXTRALEN_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_FCOMMENTLEN_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_DISKFILE_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_IATTR_OFFS, 0);
- ZipWriteInt(buf + ZIP_CENTRAL_EATTR_OFFS, 0);
- ZipWriteInt(buf + ZIP_CENTRAL_LOCALHDR_OFFS, z->offset - pos[0]);
- if ((Tcl_Write(out, buf,
- ZIP_CENTRAL_HEADER_LEN) != ZIP_CENTRAL_HEADER_LEN)
- || (Tcl_Write(out, z->name, len) != len)) {
+ z = (ZipEntry *) Tcl_GetHashValue(hPtr);
+
+ name = Tcl_UtfToExternalDString(ZipFS.utf8, z->name, -1, &ds);
+ len = Tcl_DStringLength(&ds);
+ SerializeCentralDirectoryEntry(start, end, (unsigned char *) buf,
+ z, len, dataStartOffset);
+ if ((Tcl_Write(out, buf, ZIP_CENTRAL_HEADER_LEN)
+ != ZIP_CENTRAL_HEADER_LEN)
+ || ((size_t) Tcl_Write(out, name, len) != len)) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"write error: %s", Tcl_PosixError(interp)));
+ Tcl_DStringFree(&ds);
goto done;
}
+ Tcl_DStringFree(&ds);
count++;
}
+
+ /*
+ * Finalize the central directory.
+ */
+
Tcl_Flush(out);
- pos[2] = Tcl_Tell(out);
- ZipWriteInt(buf + ZIP_CENTRAL_END_SIG_OFFS, ZIP_CENTRAL_END_SIG);
- ZipWriteShort(buf + ZIP_CENTRAL_DISKNO_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_DISKDIR_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_ENTS_OFFS, count);
- ZipWriteShort(buf + ZIP_CENTRAL_TOTALENTS_OFFS, count);
- ZipWriteInt(buf + ZIP_CENTRAL_DIRSIZE_OFFS, pos[2] - pos[1]);
- ZipWriteInt(buf + ZIP_CENTRAL_DIRSTART_OFFS, pos[1] - pos[0]);
- ZipWriteShort(buf + ZIP_CENTRAL_COMMENTLEN_OFFS, 0);
+ suffixStartOffset = Tcl_Tell(out);
+ SerializeCentralDirectorySuffix(start, end, (unsigned char *) buf,
+ count, dataStartOffset, directoryStartOffset, suffixStartOffset);
if (Tcl_Write(out, buf, ZIP_CENTRAL_END_LEN) != ZIP_CENTRAL_END_LEN) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"write error: %s", Tcl_PosixError(interp)));
@@ -2694,14 +3240,14 @@ ZipFSMkZipOrImgObjCmd(
done:
if (ret == TCL_OK) {
- ret = Tcl_CloseEx(interp, out, 0);
+ ret = Tcl_Close(interp, out);
} else {
- Tcl_CloseEx(interp, out, 0);
+ Tcl_Close(interp, out);
}
Tcl_DecrRefCount(list);
for (hPtr = Tcl_FirstHashEntry(&fileHash, &search); hPtr;
hPtr = Tcl_NextHashEntry(&search)) {
- z = (ZipEntry *)Tcl_GetHashValue(hPtr);
+ z = (ZipEntry *) Tcl_GetHashValue(hPtr);
Tcl_Free(z);
Tcl_DeleteHashEntry(hPtr);
}
@@ -2710,18 +3256,207 @@ ZipFSMkZipOrImgObjCmd(
}
/*
+ * ---------------------------------------------------------------------
+ *
+ * CopyImageFile --
+ *
+ * A simple file copy function that is used (by ZipFSMkZipOrImg) for
+ * anything that is not an image with a ZIP appended.
+ *
+ * Returns:
+ * A Tcl result code.
+ *
+ * Side effects:
+ * Writes to an output channel.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+static int
+CopyImageFile(
+ Tcl_Interp *interp, /* For error reporting. */
+ const char *imgName, /* Where to copy from. */
+ Tcl_Channel out) /* Where to copy to; already open for writing
+ * binary data. */
+{
+ size_t i, k;
+ int m, n;
+ Tcl_Channel in;
+ char buf[4096];
+ const char *errMsg;
+
+ Tcl_ResetResult(interp);
+ in = Tcl_OpenFileChannel(interp, imgName, "rb", 0644);
+ if (!in) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Get the length of the file (and exclude non-files).
+ */
+
+ i = Tcl_Seek(in, 0, SEEK_END);
+ if (i == ERROR_LENGTH) {
+ errMsg = "seek error";
+ goto copyError;
+ }
+ Tcl_Seek(in, 0, SEEK_SET);
+
+ /*
+ * Copy the whole file, 8 blocks at a time (reasonably efficient). Note
+ * that this totally ignores things like Windows's Alternate File Streams.
+ */
+
+ for (k = 0; k < i; k += m) {
+ m = i - k;
+ if (m > (int) sizeof(buf)) {
+ m = (int) sizeof(buf);
+ }
+ n = Tcl_Read(in, buf, m);
+ if (n == -1) {
+ errMsg = "read error";
+ goto copyError;
+ } else if (n == 0) {
+ break;
+ }
+ m = Tcl_Write(out, buf, n);
+ if (m != n) {
+ errMsg = "write error";
+ goto copyError;
+ }
+ }
+ Tcl_Close(interp, in);
+ return TCL_OK;
+
+ copyError:
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "%s: %s", errMsg, Tcl_PosixError(interp)));
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ *
+ * SerializeLocalEntryHeader, SerializeCentralDirectoryEntry,
+ * SerializeCentralDirectorySuffix --
+ *
+ * Create serialized forms of the structures that make up the ZIP
+ * metadata. Note that the both the local entry and the central directory
+ * entry need to have the name of the entry written directly afterwards.
+ *
+ * We could write these as structs except we need to guarantee that we
+ * are writing these out as little-endian values.
+ *
+ * Side effects:
+ * Both update their buffer arguments, but otherwise change nothing.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+static void
+SerializeLocalEntryHeader(
+ const unsigned char *start, /* The start of writable memory. */
+ const unsigned char *end, /* The end of writable memory. */
+ unsigned char *buf, /* Where to serialize to */
+ ZipEntry *z, /* The description of what to serialize. */
+ int nameLength, /* The length of the name. */
+ int align) /* The number of alignment bytes. */
+{
+ ZipWriteInt(start, end, buf + ZIP_LOCAL_SIG_OFFS, ZIP_LOCAL_HEADER_SIG);
+ ZipWriteShort(start, end, buf + ZIP_LOCAL_VERSION_OFFS, ZIP_MIN_VERSION);
+ ZipWriteShort(start, end, buf + ZIP_LOCAL_FLAGS_OFFS, z->isEncrypted);
+ ZipWriteShort(start, end, buf + ZIP_LOCAL_COMPMETH_OFFS,
+ z->compressMethod);
+ ZipWriteShort(start, end, buf + ZIP_LOCAL_MTIME_OFFS,
+ ToDosTime(z->timestamp));
+ ZipWriteShort(start, end, buf + ZIP_LOCAL_MDATE_OFFS,
+ ToDosDate(z->timestamp));
+ ZipWriteInt(start, end, buf + ZIP_LOCAL_CRC32_OFFS, z->crc32);
+ ZipWriteInt(start, end, buf + ZIP_LOCAL_COMPLEN_OFFS,
+ z->numCompressedBytes);
+ ZipWriteInt(start, end, buf + ZIP_LOCAL_UNCOMPLEN_OFFS, z->numBytes);
+ ZipWriteShort(start, end, buf + ZIP_LOCAL_PATHLEN_OFFS, nameLength);
+ ZipWriteShort(start, end, buf + ZIP_LOCAL_EXTRALEN_OFFS, align);
+}
+
+static void
+SerializeCentralDirectoryEntry(
+ const unsigned char *start, /* The start of writable memory. */
+ const unsigned char *end, /* The end of writable memory. */
+ unsigned char *buf, /* Where to serialize to */
+ ZipEntry *z, /* The description of what to serialize. */
+ size_t nameLength, /* The length of the name. */
+ long long dataStartOffset) /* The overall file offset of the start of the
+ * data section of the file. */
+{
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_SIG_OFFS,
+ ZIP_CENTRAL_HEADER_SIG);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_VERSIONMADE_OFFS,
+ ZIP_MIN_VERSION);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_VERSION_OFFS, ZIP_MIN_VERSION);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_FLAGS_OFFS, z->isEncrypted);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_COMPMETH_OFFS,
+ z->compressMethod);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_MTIME_OFFS,
+ ToDosTime(z->timestamp));
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_MDATE_OFFS,
+ ToDosDate(z->timestamp));
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_CRC32_OFFS, z->crc32);
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_COMPLEN_OFFS,
+ z->numCompressedBytes);
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_UNCOMPLEN_OFFS, z->numBytes);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_PATHLEN_OFFS, nameLength);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_EXTRALEN_OFFS, 0);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_FCOMMENTLEN_OFFS, 0);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_DISKFILE_OFFS, 0);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_IATTR_OFFS, 0);
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_EATTR_OFFS, 0);
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_LOCALHDR_OFFS,
+ z->offset - dataStartOffset);
+}
+
+static void
+SerializeCentralDirectorySuffix(
+ const unsigned char *start, /* The start of writable memory. */
+ const unsigned char *end, /* The end of writable memory. */
+ unsigned char *buf, /* Where to serialize to */
+ int entryCount, /* The number of entries in the directory */
+ long long dataStartOffset, /* The overall file offset of the start of the
+ * data section of the file. */
+ long long directoryStartOffset,
+ /* The overall file offset of the start of the
+ * central directory. */
+ long long suffixStartOffset)/* The overall file offset of the start of the
+ * suffix of the central directory (i.e.,
+ * where this data will be written). */
+{
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_END_SIG_OFFS,
+ ZIP_CENTRAL_END_SIG);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_DISKNO_OFFS, 0);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_DISKDIR_OFFS, 0);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_ENTS_OFFS, entryCount);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_TOTALENTS_OFFS, entryCount);
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_DIRSIZE_OFFS,
+ suffixStartOffset - directoryStartOffset);
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_DIRSTART_OFFS,
+ directoryStartOffset - dataStartOffset);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_COMMENTLEN_OFFS, 0);
+}
+
+/*
*-------------------------------------------------------------------------
*
* ZipFSMkZipObjCmd, ZipFSLMkZipObjCmd --
*
* These procedures are invoked to process the [zipfs mkzip] and [zipfs
- * lmkzip] commands. See description of ZipFSMkZipOrImgCmd().
+ * lmkzip] commands. See description of ZipFSMkZipOrImg().
*
* Results:
* A standard Tcl result.
*
* Side effects:
- * See description of ZipFSMkZipOrImgCmd().
+ * See description of ZipFSMkZipOrImg().
*
*-------------------------------------------------------------------------
*/
@@ -2733,17 +3468,22 @@ ZipFSMkZipObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ Tcl_Obj *stripPrefix, *password;
+
if (objc < 3 || objc > 5) {
Tcl_WrongNumArgs(interp, 1, objv, "outfile indir ?strip? ?password?");
return TCL_ERROR;
}
if (Tcl_IsSafe(interp)) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "operation not permitted in a safe interpreter", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "SAFE_INTERP", NULL);
+ ZIPFS_ERROR(interp, "operation not permitted in a safe interpreter");
+ ZIPFS_ERROR_CODE(interp, "SAFE_INTERP");
return TCL_ERROR;
}
- return ZipFSMkZipOrImgObjCmd(interp, 0, 0, objc, objv);
+
+ stripPrefix = (objc > 3 ? objv[3] : NULL);
+ password = (objc > 4 ? objv[4] : NULL);
+ return ZipFSMkZipOrImg(interp, 0, objv[1], objv[2], NULL, NULL,
+ stripPrefix, password);
}
static int
@@ -2753,17 +3493,21 @@ ZipFSLMkZipObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ Tcl_Obj *password;
+
if (objc < 3 || objc > 4) {
Tcl_WrongNumArgs(interp, 1, objv, "outfile inlist ?password?");
return TCL_ERROR;
}
if (Tcl_IsSafe(interp)) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "operation not permitted in a safe interpreter", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "SAFE_INTERP", NULL);
+ ZIPFS_ERROR(interp, "operation not permitted in a safe interpreter");
+ ZIPFS_ERROR_CODE(interp, "SAFE_INTERP");
return TCL_ERROR;
}
- return ZipFSMkZipOrImgObjCmd(interp, 0, 1, objc, objv);
+
+ password = (objc > 3 ? objv[3] : NULL);
+ return ZipFSMkZipOrImg(interp, 0, objv[1], NULL, objv[2], NULL,
+ NULL, password);
}
/*
@@ -2772,13 +3516,13 @@ ZipFSLMkZipObjCmd(
* ZipFSMkImgObjCmd, ZipFSLMkImgObjCmd --
*
* These procedures are invoked to process the [zipfs mkimg] and [zipfs
- * lmkimg] commands. See description of ZipFSMkZipOrImgCmd().
+ * lmkimg] commands. See description of ZipFSMkZipOrImg().
*
* Results:
* A standard Tcl result.
*
* Side effects:
- * See description of ZipFSMkZipOrImgCmd().
+ * See description of ZipFSMkZipOrImg().
*
*-------------------------------------------------------------------------
*/
@@ -2790,18 +3534,24 @@ ZipFSMkImgObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ Tcl_Obj *originFile, *stripPrefix, *password;
+
if (objc < 3 || objc > 6) {
Tcl_WrongNumArgs(interp, 1, objv,
"outfile indir ?strip? ?password? ?infile?");
return TCL_ERROR;
}
if (Tcl_IsSafe(interp)) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "operation not permitted in a safe interpreter", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "SAFE_INTERP", NULL);
+ ZIPFS_ERROR(interp, "operation not permitted in a safe interpreter");
+ ZIPFS_ERROR_CODE(interp, "SAFE_INTERP");
return TCL_ERROR;
}
- return ZipFSMkZipOrImgObjCmd(interp, 1, 0, objc, objv);
+
+ originFile = (objc > 5 ? objv[5] : NULL);
+ stripPrefix = (objc > 3 ? objv[3] : NULL);
+ password = (objc > 4 ? objv[4] : NULL);
+ return ZipFSMkZipOrImg(interp, 1, objv[1], objv[2], NULL,
+ originFile, stripPrefix, password);
}
static int
@@ -2811,17 +3561,22 @@ ZipFSLMkImgObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ Tcl_Obj *originFile, *password;
+
if (objc < 3 || objc > 5) {
Tcl_WrongNumArgs(interp, 1, objv, "outfile inlist ?password infile?");
return TCL_ERROR;
}
if (Tcl_IsSafe(interp)) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "operation not permitted in a safe interpreter", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "SAFE_INTERP", NULL);
+ ZIPFS_ERROR(interp, "operation not permitted in a safe interpreter");
+ ZIPFS_ERROR_CODE(interp, "SAFE_INTERP");
return TCL_ERROR;
}
- return ZipFSMkZipOrImgObjCmd(interp, 1, 1, objc, objv);
+
+ originFile = (objc > 4 ? objv[4] : NULL);
+ password = (objc > 3 ? objv[3] : NULL);
+ return ZipFSMkZipOrImg(interp, 1, objv[1], NULL, objv[2],
+ originFile, NULL, password);
}
/*
@@ -2936,7 +3691,7 @@ ZipFSExistsObjCmd(
*
* ZipFSInfoObjCmd --
*
- * This procedure is invoked to process the [zipfs info] command. On
+ * This procedure is invoked to process the [zipfs info] command. On
* success, it returns a Tcl list made up of name of ZIP archive file,
* size uncompressed, size compressed, and archive offset of a file in
* the ZIP filesystem.
@@ -3012,36 +3767,48 @@ ZipFSListObjCmd(
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
Tcl_Obj *result = Tcl_GetObjResult(interp);
+ const char *options[] = {"-glob", "-regexp", NULL};
+ enum list_options { OPT_GLOB, OPT_REGEXP };
+
+ /*
+ * Parse arguments.
+ */
if (objc > 3) {
Tcl_WrongNumArgs(interp, 1, objv, "?(-glob|-regexp)? ?pattern?");
return TCL_ERROR;
}
if (objc == 3) {
- size_t n;
- char *what = Tcl_GetStringFromObj(objv[1], &n);
+ int idx;
- if ((n >= 2) && (strncmp(what, "-glob", n) == 0)) {
+ if (Tcl_GetIndexFromObj(interp, objv[1], options, "option",
+ 0, &idx) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch (idx) {
+ case OPT_GLOB:
pattern = TclGetString(objv[2]);
- } else if ((n >= 2) && (strncmp(what, "-regexp", n) == 0)) {
+ break;
+ case OPT_REGEXP:
regexp = Tcl_RegExpCompile(interp, TclGetString(objv[2]));
if (!regexp) {
return TCL_ERROR;
}
- } else {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "unknown option \"%s\"", what));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "BAD_OPT", NULL);
- return TCL_ERROR;
+ break;
}
} else if (objc == 2) {
pattern = TclGetString(objv[1]);
}
+
+ /*
+ * Scan for matching entries.
+ */
+
ReadLock();
if (pattern) {
for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- ZipEntry *z = (ZipEntry *)Tcl_GetHashValue(hPtr);
+ ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
if (Tcl_StringMatch(z->name, pattern)) {
Tcl_ListObjAppendElement(interp, result,
@@ -3051,7 +3818,7 @@ ZipFSListObjCmd(
} else if (regexp) {
for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
hPtr; hPtr = Tcl_NextHashEntry(&search)) {
- ZipEntry *z = (ZipEntry *)Tcl_GetHashValue(hPtr);
+ ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
if (Tcl_RegExpExec(interp, regexp, z->name, z->name)) {
Tcl_ListObjAppendElement(interp, result,
@@ -3061,7 +3828,7 @@ ZipFSListObjCmd(
} else {
for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
hPtr; hPtr = Tcl_NextHashEntry(&search)) {
- ZipEntry *z = (ZipEntry *)Tcl_GetHashValue(hPtr);
+ ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
Tcl_ListObjAppendElement(interp, result,
Tcl_NewStringObj(z->name, -1));
@@ -3221,7 +3988,7 @@ ZipChannelClose(
TCL_UNUSED(Tcl_Interp *),
int flags)
{
- ZipChannel *info = (ZipChannel *)instanceData;
+ ZipChannel *info = (ZipChannel *) instanceData;
if ((flags & (TCL_CLOSE_READ | TCL_CLOSE_WRITE)) != 0) {
return EINVAL;
@@ -3237,7 +4004,8 @@ ZipChannelClose(
}
if (info->isWriting) {
ZipEntry *z = info->zipEntryPtr;
- unsigned char *newdata = (unsigned char *)Tcl_AttemptRealloc(info->ubuf, info->numRead);
+ unsigned char *newdata = (unsigned char *)
+ Tcl_AttemptRealloc(info->ubuf, info->numRead);
if (newdata) {
if (z->data) {
@@ -3452,6 +4220,18 @@ ZipChannelWideSeek(
info->numRead = (size_t) offset;
return info->numRead;
}
+
+#if !defined(TCL_NO_DEPRECATED) && (TCL_MAJOR_VERSION < 9)
+static int
+ZipChannelSeek(
+ void *instanceData,
+ long offset,
+ int mode,
+ int *errloc)
+{
+ return ZipChannelWideSeek(instanceData, offset, mode, errloc);
+}
+#endif
/*
*-------------------------------------------------------------------------
@@ -3510,7 +4290,7 @@ ZipChannelGetFile(
* ZipChannelOpen --
*
* This function opens a Tcl_Channel on a file from a mounted ZIP archive
- * according to given open mode.
+ * according to given open mode (already parsed by caller).
*
* Results:
* Tcl_Channel on success, or NULL on error.
@@ -3524,24 +4304,19 @@ ZipChannelGetFile(
static Tcl_Channel
ZipChannelOpen(
Tcl_Interp *interp, /* Current interpreter. */
- char *filename,
- int mode,
- TCL_UNUSED(int) /*permissions*/)
+ char *filename, /* What are we opening. */
+ int wr, /* True if we're opening in write mode. */
+ int trunc) /* True if we're opening in truncate mode. */
{
ZipEntry *z;
ZipChannel *info;
- int i, ch, trunc, wr, flags = 0;
+ int flags = 0;
char cname[128];
- if ((mode & O_APPEND)
- || ((ZipFS.wrmax <= 0) && (mode & (O_WRONLY | O_RDWR)))) {
- if (interp) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("unsupported open mode", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "BAD_MODE", NULL);
- }
- return NULL;
- }
+ /*
+ * Is the file there?
+ */
+
WriteLock();
z = ZipFSLookup(filename);
if (!z) {
@@ -3553,188 +4328,161 @@ ZipChannelOpen(
}
goto error;
}
- trunc = (mode & O_TRUNC) != 0;
- wr = (mode & (O_WRONLY | O_RDWR)) != 0;
- if ((z->compressMethod != ZIP_COMPMETH_STORED)
- && (z->compressMethod != ZIP_COMPMETH_DEFLATED)) {
- ZIPFS_ERROR(interp, "unsupported compression method");
+
+ /*
+ * Do we support opening the file that way?
+ */
+
+ if (wr && z->isDirectory) {
+ Tcl_SetErrno(EISDIR);
if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "COMP_METHOD", NULL);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "unsupported file type: %s",
+ Tcl_PosixError(interp)));
}
goto error;
}
- if (wr && z->isDirectory) {
- ZIPFS_ERROR(interp, "unsupported file type");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "FILE_TYPE", NULL);
- }
+ if ((z->compressMethod != ZIP_COMPMETH_STORED)
+ && (z->compressMethod != ZIP_COMPMETH_DEFLATED)) {
+ ZIPFS_ERROR(interp, "unsupported compression method");
+ ZIPFS_ERROR_CODE(interp, "COMP_METHOD");
goto error;
}
if (!trunc) {
flags |= TCL_READABLE;
if (z->isEncrypted && (z->zipFilePtr->passBuf[0] == 0)) {
ZIPFS_ERROR(interp, "decryption failed");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "DECRYPT", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "DECRYPT");
goto error;
} else if (wr && !z->data && (z->numBytes > ZipFS.wrmax)) {
ZIPFS_ERROR(interp, "file too large");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "FILE_SIZE", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "FILE_SIZE");
goto error;
}
} else {
flags = TCL_WRITABLE;
}
- info = (ZipChannel *)Tcl_AttemptAlloc(sizeof(ZipChannel));
+
+ info = AllocateZipChannel(interp);
if (!info) {
- ZIPFS_ERROR(interp, "out of memory");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
goto error;
}
info->zipFilePtr = z->zipFilePtr;
info->zipEntryPtr = z;
- info->numRead = 0;
if (wr) {
+ /*
+ * Set up a writable channel.
+ */
+
flags |= TCL_WRITABLE;
- info->isWriting = 1;
- info->isDirectory = 0;
- info->maxWrite = ZipFS.wrmax;
- info->iscompr = 0;
- info->isEncrypted = 0;
- info->ubuf = (unsigned char *)Tcl_AttemptAlloc(info->maxWrite);
- if (!info->ubuf) {
- merror0:
- if (info->ubuf) {
- Tcl_Free(info->ubuf);
- }
+ if (InitWritableChannel(interp, info, z, trunc) == TCL_ERROR) {
Tcl_Free(info);
- ZIPFS_ERROR(interp, "out of memory");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
goto error;
}
- memset(info->ubuf, 0, info->maxWrite);
- if (trunc) {
- info->numBytes = 0;
- } else if (z->data) {
- size_t j = z->numBytes;
-
- if (j > info->maxWrite) {
- j = info->maxWrite;
- }
- memcpy(info->ubuf, z->data, j);
- info->numBytes = j;
- } else {
- unsigned char *zbuf = z->zipFilePtr->data + z->offset;
-
- if (z->isEncrypted) {
- int len = z->zipFilePtr->passBuf[0] & 0xFF;
- char passBuf[260];
-
- for (i = 0; i < len; i++) {
- ch = z->zipFilePtr->passBuf[len - i];
- passBuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
- }
- passBuf[i] = '\0';
- init_keys(passBuf, info->keys, crc32tab);
- memset(passBuf, 0, sizeof(passBuf));
- for (i = 0; i < 12; i++) {
- ch = info->ubuf[i];
- zdecode(info->keys, crc32tab, ch);
- }
- zbuf += i;
- }
- if (z->compressMethod == ZIP_COMPMETH_DEFLATED) {
- z_stream stream;
- int err;
- unsigned char *cbuf = NULL;
-
- memset(&stream, 0, sizeof(z_stream));
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
- stream.avail_in = z->numCompressedBytes;
- if (z->isEncrypted) {
- size_t j;
-
- stream.avail_in -= 12;
- cbuf = (unsigned char *)Tcl_AttemptAlloc(stream.avail_in);
- if (!cbuf) {
- goto merror0;
- }
- for (j = 0; j < stream.avail_in; j++) {
- ch = info->ubuf[j];
- cbuf[j] = zdecode(info->keys, crc32tab, ch);
- }
- stream.next_in = cbuf;
- } else {
- stream.next_in = zbuf;
- }
- stream.next_out = info->ubuf;
- stream.avail_out = info->maxWrite;
- 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) {
- memset(info->keys, 0, sizeof(info->keys));
- Tcl_Free(cbuf);
- }
- goto wrapchan;
- }
- cerror0:
- if (cbuf) {
- memset(info->keys, 0, sizeof(info->keys));
- Tcl_Free(cbuf);
- }
- if (info->ubuf) {
- Tcl_Free(info->ubuf);
- }
- Tcl_Free(info);
- ZIPFS_ERROR(interp, "decompression error");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "CORRUPT", NULL);
- }
- goto error;
- } else if (z->isEncrypted) {
- for (i = 0; i < z->numBytes - 12; i++) {
- ch = zbuf[i];
- info->ubuf[i] = zdecode(info->keys, crc32tab, ch);
- }
- } else {
- memcpy(info->ubuf, zbuf, z->numBytes);
- }
- memset(info->keys, 0, sizeof(info->keys));
- goto wrapchan;
- }
} else if (z->data) {
+ /*
+ * Set up a readable channel for direct data.
+ */
+
flags |= TCL_READABLE;
- info->isWriting = 0;
- info->iscompr = 0;
- info->isDirectory = 0;
- info->isEncrypted = 0;
info->numBytes = z->numBytes;
- info->maxWrite = 0;
info->ubuf = z->data;
} else {
+ /*
+ * Set up a readable channel.
+ */
+
flags |= TCL_READABLE;
- info->isWriting = 0;
- info->iscompr = (z->compressMethod == ZIP_COMPMETH_DEFLATED);
- info->ubuf = z->zipFilePtr->data + z->offset;
- info->isDirectory = z->isDirectory;
- info->isEncrypted = z->isEncrypted;
- info->numBytes = z->numBytes;
- info->maxWrite = 0;
- if (info->isEncrypted) {
+ if (InitReadableChannel(interp, info, z) == TCL_ERROR) {
+ Tcl_Free(info);
+ goto error;
+ }
+ }
+
+ /*
+ * Wrap the ZipChannel into a Tcl_Channel.
+ */
+
+ sprintf(cname, "zipfs_%" TCL_Z_MODIFIER "x_%d", z->offset,
+ ZipFS.idCount++);
+ z->zipFilePtr->numOpen++;
+ Unlock();
+ return Tcl_CreateChannel(&ZipChannelType, cname, info, flags);
+
+ error:
+ Unlock();
+ return NULL;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * InitWritableChannel --
+ *
+ * Assistant for ZipChannelOpen() that sets up a writable channel. It's
+ * up to the caller to actually register the channel.
+ *
+ * Returns:
+ * Tcl result code.
+ *
+ * Side effects:
+ * Allocates memory for the implementation of the channel. Writes to the
+ * interpreter's result on error.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int
+InitWritableChannel(
+ Tcl_Interp *interp, /* Current interpreter, or NULL (when errors
+ * will be silent). */
+ ZipChannel *info, /* The channel to set up. */
+ ZipEntry *z, /* The zipped file that the channel will write
+ * to. */
+ int trunc) /* Whether to truncate the data. */
+{
+ int i, ch;
+ unsigned char *cbuf = NULL;
+
+ /*
+ * Set up a writable channel.
+ */
+
+ info->isWriting = 1;
+ info->maxWrite = ZipFS.wrmax;
+
+ info->ubuf = (unsigned char *) Tcl_AttemptAlloc(info->maxWrite);
+ if (!info->ubuf) {
+ goto memoryError;
+ }
+ memset(info->ubuf, 0, info->maxWrite);
+
+ if (trunc) {
+ /*
+ * Truncate; nothing there.
+ */
+
+ info->numBytes = 0;
+ } else if (z->data) {
+ /*
+ * Already got uncompressed data.
+ */
+
+ unsigned int j = z->numBytes;
+
+ if (j > info->maxWrite) {
+ j = info->maxWrite;
+ }
+ memcpy(info->ubuf, z->data, j);
+ info->numBytes = j;
+ } else {
+ /*
+ * Need to uncompress the existing data.
+ */
+
+ unsigned char *zbuf = z->zipFilePtr->data + z->offset;
+
+ if (z->isEncrypted) {
int len = z->zipFilePtr->passBuf[0] & 0xFF;
char passBuf[260];
@@ -3749,118 +4497,244 @@ ZipChannelOpen(
ch = info->ubuf[i];
zdecode(info->keys, crc32tab, ch);
}
- info->ubuf += i;
+ zbuf += i;
}
- if (info->iscompr) {
+
+ if (z->compressMethod == ZIP_COMPMETH_DEFLATED) {
z_stream stream;
int err;
- unsigned char *ubuf = NULL;
- size_t j;
memset(&stream, 0, sizeof(z_stream));
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.avail_in = z->numCompressedBytes;
- if (info->isEncrypted) {
+ if (z->isEncrypted) {
+ unsigned int j;
+
stream.avail_in -= 12;
- ubuf = (unsigned char *)Tcl_AttemptAlloc(stream.avail_in);
- if (!ubuf) {
- info->ubuf = NULL;
- goto merror;
+ cbuf = (unsigned char *) Tcl_AttemptAlloc(stream.avail_in);
+ if (!cbuf) {
+ goto memoryError;
}
for (j = 0; j < stream.avail_in; j++) {
ch = info->ubuf[j];
- ubuf[j] = zdecode(info->keys, crc32tab, ch);
+ cbuf[j] = zdecode(info->keys, crc32tab, ch);
}
- stream.next_in = ubuf;
+ stream.next_in = cbuf;
} else {
- stream.next_in = info->ubuf;
+ stream.next_in = zbuf;
}
- stream.next_out = info->ubuf = (unsigned char *)Tcl_AttemptAlloc(info->numBytes);
- if (!info->ubuf) {
- merror:
- if (ubuf) {
- info->isEncrypted = 0;
- memset(info->keys, 0, sizeof(info->keys));
- Tcl_Free(ubuf);
- }
- Tcl_Free(info);
- if (interp) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("out of memory", -1));
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
- goto error;
- }
- stream.avail_out = info->numBytes;
+ stream.next_out = info->ubuf;
+ stream.avail_out = info->maxWrite;
if (inflateInit2(&stream, -15) != Z_OK) {
- goto cerror;
+ goto corruptionError;
}
err = inflate(&stream, Z_SYNC_FLUSH);
inflateEnd(&stream);
if ((err == Z_STREAM_END)
|| ((err == Z_OK) && (stream.avail_in == 0))) {
- if (ubuf) {
- info->isEncrypted = 0;
+ if (cbuf) {
memset(info->keys, 0, sizeof(info->keys));
- Tcl_Free(ubuf);
+ Tcl_Free(cbuf);
}
- goto wrapchan;
- }
- cerror:
- if (ubuf) {
- info->isEncrypted = 0;
- memset(info->keys, 0, sizeof(info->keys));
- Tcl_Free(ubuf);
- }
- if (info->ubuf) {
- Tcl_Free(info->ubuf);
- }
- Tcl_Free(info);
- ZIPFS_ERROR(interp, "decompression error");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "CORRUPT", NULL);
+ return TCL_OK;
}
- goto error;
- } else if (info->isEncrypted) {
- unsigned char *ubuf = NULL;
- size_t j, len;
+ goto corruptionError;
+ } else if (z->isEncrypted) {
+ /*
+ * Need to decrypt some otherwise-simple stored data.
+ */
+ for (i = 0; i < z->numBytes - 12; i++) {
+ ch = zbuf[i];
+ info->ubuf[i] = zdecode(info->keys, crc32tab, ch);
+ }
+ } else {
/*
- * Decode encrypted but uncompressed file, since we support
- * Tcl_Seek() on it, and it can be randomly accessed later.
+ * Simple stored data. Copy into our working buffer.
*/
- len = z->numCompressedBytes - 12;
- ubuf = (unsigned char *) Tcl_AttemptAlloc(len);
- if (ubuf == NULL) {
- Tcl_Free((char *) info);
- if (interp != NULL) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("out of memory", -1));
- }
- goto error;
+ memcpy(info->ubuf, zbuf, z->numBytes);
+ }
+ memset(info->keys, 0, sizeof(info->keys));
+ }
+ return TCL_OK;
+
+ memoryError:
+ if (info->ubuf) {
+ Tcl_Free(info->ubuf);
+ }
+ ZIPFS_MEM_ERROR(interp);
+ return TCL_ERROR;
+
+ corruptionError:
+ if (cbuf) {
+ memset(info->keys, 0, sizeof(info->keys));
+ Tcl_Free(cbuf);
+ }
+ if (info->ubuf) {
+ Tcl_Free(info->ubuf);
+ }
+ ZIPFS_ERROR(interp, "decompression error");
+ ZIPFS_ERROR_CODE(interp, "CORRUPT");
+ return TCL_ERROR;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * InitReadableChannel --
+ *
+ * Assistant for ZipChannelOpen() that sets up a readable channel. It's
+ * up to the caller to actually register the channel.
+ *
+ * Returns:
+ * Tcl result code.
+ *
+ * Side effects:
+ * Allocates memory for the implementation of the channel. Writes to the
+ * interpreter's result on error.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int
+InitReadableChannel(
+ Tcl_Interp *interp, /* Current interpreter, or NULL (when errors
+ * will be silent). */
+ ZipChannel *info, /* The channel to set up. */
+ ZipEntry *z) /* The zipped file that the channel will read
+ * from. */
+{
+ unsigned char *ubuf = NULL;
+ int i, ch;
+
+ info->iscompr = (z->compressMethod == ZIP_COMPMETH_DEFLATED);
+ info->ubuf = z->zipFilePtr->data + z->offset;
+ info->isDirectory = z->isDirectory;
+ info->isEncrypted = z->isEncrypted;
+ info->numBytes = z->numBytes;
+
+ if (info->isEncrypted) {
+ int len = z->zipFilePtr->passBuf[0] & 0xFF;
+ char passBuf[260];
+
+ for (i = 0; i < len; i++) {
+ ch = z->zipFilePtr->passBuf[len - i];
+ passBuf[i] = (ch & 0x0f) | pwrot[(ch >> 4) & 0x0f];
+ }
+ passBuf[i] = '\0';
+ init_keys(passBuf, info->keys, crc32tab);
+ memset(passBuf, 0, sizeof(passBuf));
+ 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 int j;
+
+ /*
+ * Data to decode is compressed, and possibly encrpyted too.
+ */
+
+ memset(&stream, 0, sizeof(z_stream));
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+ stream.avail_in = z->numCompressedBytes;
+ if (info->isEncrypted) {
+ stream.avail_in -= 12;
+ ubuf = (unsigned char *) Tcl_AttemptAlloc(stream.avail_in);
+ if (!ubuf) {
+ info->ubuf = NULL;
+ goto memoryError;
}
- for (j = 0; j < len; j++) {
+
+ for (j = 0; j < stream.avail_in; j++) {
ch = info->ubuf[j];
ubuf[j] = zdecode(info->keys, crc32tab, ch);
}
- info->ubuf = ubuf;
+ stream.next_in = ubuf;
+ } else {
+ stream.next_in = info->ubuf;
+ }
+ stream.next_out = info->ubuf = (unsigned char *)
+ Tcl_AttemptAlloc(info->numBytes);
+ if (!info->ubuf) {
+ goto memoryError;
+ }
+ stream.avail_out = info->numBytes;
+ if (inflateInit2(&stream, -15) != Z_OK) {
+ goto corruptionError;
+ }
+ err = inflate(&stream, Z_SYNC_FLUSH);
+ inflateEnd(&stream);
+
+ /*
+ * Decompression was successful if we're either in the END state, or
+ * in the OK state with no buffered bytes.
+ */
+
+ if ((err != Z_STREAM_END)
+ && ((err != Z_OK) || (stream.avail_in != 0))) {
+ goto corruptionError;
+ }
+
+ if (ubuf) {
info->isEncrypted = 0;
+ memset(info->keys, 0, sizeof(info->keys));
+ Tcl_Free(ubuf);
}
+ return TCL_OK;
+ } else if (info->isEncrypted) {
+ unsigned int j, len;
+
+ /*
+ * Decode encrypted but uncompressed file, since we support Tcl_Seek()
+ * on it, and it can be randomly accessed later.
+ */
+
+ len = z->numCompressedBytes - 12;
+ ubuf = (unsigned char *) Tcl_AttemptAlloc(len);
+ if (ubuf == NULL) {
+ goto memoryError;
+ }
+ for (j = 0; j < len; j++) {
+ ch = info->ubuf[j];
+ ubuf[j] = zdecode(info->keys, crc32tab, ch);
+ }
+ info->ubuf = ubuf;
+ info->isEncrypted = 0;
}
+ return TCL_OK;
- wrapchan:
- sprintf(cname, "zipfs_%" TCL_Z_MODIFIER "x_%d", z->offset,
- ZipFS.idCount++);
- z->zipFilePtr->numOpen++;
- Unlock();
- return Tcl_CreateChannel(&ZipChannelType, cname, info, flags);
+ corruptionError:
+ if (ubuf) {
+ info->isEncrypted = 0;
+ memset(info->keys, 0, sizeof(info->keys));
+ Tcl_Free(ubuf);
+ }
+ if (info->ubuf) {
+ Tcl_Free(info->ubuf);
+ }
+ ZIPFS_ERROR(interp, "decompression error");
+ ZIPFS_ERROR_CODE(interp, "CORRUPT");
+ return TCL_ERROR;
- error:
- Unlock();
- return NULL;
+ memoryError:
+ if (ubuf) {
+ info->isEncrypted = 0;
+ memset(info->keys, 0, sizeof(info->keys));
+ Tcl_Free(ubuf);
+ }
+ ZIPFS_MEM_ERROR(interp);
+ return TCL_ERROR;
}
/*
@@ -3945,9 +4819,14 @@ ZipEntryAccess(
*
* ZipFSOpenFileChannelProc --
*
+ * Open a channel to a file in a mounted ZIP archive. Delegates to
+ * ZipChannelOpen().
+ *
* Results:
+ * Tcl_Channel on success, or NULL on error.
*
* Side effects:
+ * Allocates memory.
*
*-------------------------------------------------------------------------
*/
@@ -3957,14 +4836,31 @@ ZipFSOpenFileChannelProc(
Tcl_Interp *interp, /* Current interpreter. */
Tcl_Obj *pathPtr,
int mode,
- int permissions)
+ TCL_UNUSED(int) /* permissions */)
{
+ int trunc = (mode & O_TRUNC) != 0;
+ int wr = (mode & (O_WRONLY | O_RDWR)) != 0;
+
pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
if (!pathPtr) {
return NULL;
}
- return ZipChannelOpen(interp, TclGetString(pathPtr), mode,
- permissions);
+
+ /*
+ * Check for unsupported modes.
+ */
+
+ if ((mode & O_APPEND) || ((ZipFS.wrmax <= 0) && wr)) {
+ Tcl_SetErrno(EACCES);
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "write access not supported: %s",
+ Tcl_PosixError(interp)));
+ }
+ return NULL;
+ }
+
+ return ZipChannelOpen(interp, TclGetString(pathPtr), wr, trunc);
}
/*
@@ -3989,7 +4885,6 @@ ZipFSStatProc(
Tcl_Obj *pathPtr,
Tcl_StatBuf *buf)
{
-
pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
if (!pathPtr) {
return -1;
@@ -4055,6 +4950,38 @@ ZipFSFilesystemSeparatorProc(
/*
*-------------------------------------------------------------------------
*
+ * AppendWithPrefix --
+ *
+ * Worker for ZipFSMatchInDirectoryProc() that is a wrapper around
+ * Tcl_ListObjAppendElement() which knows about handling prefixes.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static inline void
+AppendWithPrefix(
+ Tcl_Obj *result, /* Where to append a list element to. */
+ Tcl_DString *prefix, /* The prefix to add to the element, or NULL
+ * for don't do that. */
+ const char *name, /* The name to append. */
+ int nameLen) /* The length of the name. May be -1 for
+ * append-up-to-NUL-byte. */
+{
+ if (prefix) {
+ int prefixLength = Tcl_DStringLength(prefix);
+
+ Tcl_DStringAppend(prefix, name, nameLen);
+ Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(
+ Tcl_DStringValue(prefix), Tcl_DStringLength(prefix)));
+ Tcl_DStringSetLength(prefix, prefixLength);
+ } else {
+ Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj(name, nameLen));
+ }
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
* ZipFSMatchInDirectoryProc --
*
* This routine is used by the globbing code to search a directory for
@@ -4074,136 +5001,89 @@ ZipFSFilesystemSeparatorProc(
static int
ZipFSMatchInDirectoryProc(
TCL_UNUSED(Tcl_Interp *),
- Tcl_Obj *result,
- Tcl_Obj *pathPtr,
- const char *pattern,
- Tcl_GlobTypeData *types)
+ Tcl_Obj *result, /* Where to append matched items to. */
+ Tcl_Obj *pathPtr, /* Where we are looking. */
+ const char *pattern, /* What names we are looking for. */
+ Tcl_GlobTypeData *types) /* What types we are looking for. */
{
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
Tcl_Obj *normPathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
- int scnt, l, dirOnly = -1, strip = 0;
- size_t len, prefixLen;
+ int scnt, l, dirOnly = -1, prefixLen, strip = 0, mounts = 0;
+ int len;
char *pat, *prefix, *path;
- Tcl_DString dsPref;
+ Tcl_DString dsPref, *prefixBuf = NULL;
if (!normPathPtr) {
return -1;
}
if (types) {
dirOnly = (types->type & TCL_GLOB_TYPE_DIR) == TCL_GLOB_TYPE_DIR;
+ mounts = (types->type == TCL_GLOB_TYPE_MOUNT);
}
/*
* The prefix that gets prepended to results.
*/
- prefix = Tcl_GetStringFromObj(pathPtr, &prefixLen);
+ prefix = TclGetStringFromObj(pathPtr, &prefixLen);
/*
* The (normalized) path we're searching.
*/
- path = Tcl_GetStringFromObj(normPathPtr, &len);
+ path = TclGetStringFromObj(normPathPtr, &len);
Tcl_DStringInit(&dsPref);
- Tcl_DStringAppend(&dsPref, prefix, prefixLen);
-
if (strcmp(prefix, path) == 0) {
- prefix = NULL;
+ prefixBuf = NULL;
} else {
+ /*
+ * We need to strip the normalized prefix of the filenames and replace
+ * it with the official prefix that we were expecting to get.
+ */
+
strip = len + 1;
- }
- if (prefix) {
+ Tcl_DStringAppend(&dsPref, prefix, prefixLen);
Tcl_DStringAppend(&dsPref, "/", 1);
- prefixLen++;
prefix = Tcl_DStringValue(&dsPref);
+ prefixBuf = &dsPref;
}
+
ReadLock();
- if (types && (types->type == TCL_GLOB_TYPE_MOUNT)) {
- l = CountSlashes(path);
- if (path[len - 1] == '/') {
- len--;
- } else {
- l++;
- }
- if (!pattern || (pattern[0] == '\0')) {
- pattern = "*";
- }
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- ZipFile *zf = (ZipFile *)Tcl_GetHashValue(hPtr);
-
- if (zf->mountPointLen == 0) {
- ZipEntry *z;
-
- for (z = zf->topEnts; z; z = z->tnext) {
- size_t 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) {
- 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));
- }
- }
- }
- } else if ((zf->mountPointLen > len + 1)
- && (strncmp(zf->mountPoint, path, len) == 0)
- && (zf->mountPoint[len] == '/')
- && (CountSlashes(zf->mountPoint) == l)
- && Tcl_StringCaseMatch(zf->mountPoint + len + 1,
- pattern, 0)) {
- if (prefix) {
- Tcl_DStringAppend(&dsPref, zf->mountPoint,
- zf->mountPointLen);
- Tcl_ListObjAppendElement(NULL, result,
- Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
- Tcl_DStringLength(&dsPref)));
- Tcl_DStringSetLength(&dsPref, prefixLen);
- } else {
- Tcl_ListObjAppendElement(NULL, result,
- Tcl_NewStringObj(zf->mountPoint,
- zf->mountPointLen));
- }
- }
- }
+
+ /*
+ * Are we globbing the mount points?
+ */
+
+ if (mounts) {
+ ZipFSMatchMountPoints(result, normPathPtr, pattern, prefixBuf);
goto end;
}
+ /*
+ * Can we skip the complexity of actual globbing? Without a pattern, yes;
+ * it's a directory existence test.
+ */
+
if (!pattern || (pattern[0] == '\0')) {
- hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, path);
- if (hPtr) {
- ZipEntry *z = (ZipEntry *)Tcl_GetHashValue(hPtr);
-
- if ((dirOnly < 0) || (!dirOnly && !z->isDirectory)
- || (dirOnly && z->isDirectory)) {
- if (prefix) {
- 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));
- }
- }
+ ZipEntry *z = ZipFSLookup(path);
+
+ if (z && ((dirOnly < 0) || (!dirOnly && !z->isDirectory)
+ || (dirOnly && z->isDirectory))) {
+ AppendWithPrefix(result, prefixBuf, z->name, -1);
}
goto end;
}
+ /*
+ * We've got to work for our supper and do the actual globbing. And all
+ * we've got really is an undifferentiated pile of all the filenames we've
+ * got from all our ZIP mounts.
+ */
+
l = strlen(pattern);
- pat = (char *)Tcl_Alloc(len + l + 2);
+ pat = (char *) Tcl_Alloc(len + l + 2);
memcpy(pat, path, len);
while ((len > 1) && (pat[len - 1] == '/')) {
--len;
@@ -4214,25 +5094,17 @@ ZipFSMatchInDirectoryProc(
}
memcpy(pat + len, pattern, l + 1);
scnt = CountSlashes(pat);
+
for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
hPtr; hPtr = Tcl_NextHashEntry(&search)) {
- ZipEntry *z = (ZipEntry *)Tcl_GetHashValue(hPtr);
+ ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);
if ((dirOnly >= 0) && ((dirOnly && !z->isDirectory)
|| (!dirOnly && z->isDirectory))) {
continue;
}
if ((z->depth == scnt) && Tcl_StringCaseMatch(z->name, pat, 0)) {
- if (prefix) {
- 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));
- }
+ AppendWithPrefix(result, prefixBuf, z->name + strip, -1);
}
}
Tcl_Free(pat);
@@ -4246,6 +5118,94 @@ ZipFSMatchInDirectoryProc(
/*
*-------------------------------------------------------------------------
*
+ * ZipFSMatchMountPoints --
+ *
+ * This routine is a worker for ZipFSMatchInDirectoryProc, used by the
+ * globbing code to search for all mount points files which match a given
+ * pattern.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Adds the matching mounts to the list in result, uses prefix as working
+ * space if it is non-NULL.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static void
+ZipFSMatchMountPoints(
+ Tcl_Obj *result, /* The list of matches being built. */
+ Tcl_Obj *normPathPtr, /* Where we're looking from. */
+ const char *pattern, /* What we're looking for. NULL for a full
+ * list. */
+ Tcl_DString *prefix) /* Workspace filled with a prefix for all the
+ * filenames, or NULL if no prefix is to be
+ * used. */
+{
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ int l, normLength;
+ const char *path = TclGetStringFromObj(normPathPtr, &normLength);
+ size_t len = (size_t) normLength;
+
+ if (len < 1) {
+ /*
+ * Shouldn't happen. But "shouldn't"...
+ */
+
+ return;
+ }
+ l = CountSlashes(path);
+ if (path[len - 1] == '/') {
+ len--;
+ } else {
+ l++;
+ }
+ if (!pattern || (pattern[0] == '\0')) {
+ pattern = "*";
+ }
+
+ for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
+ hPtr = Tcl_NextHashEntry(&search)) {
+ ZipFile *zf = (ZipFile *) Tcl_GetHashValue(hPtr);
+
+ if (zf->mountPointLen == 0) {
+ ZipEntry *z;
+
+ /*
+ * Enumerate the contents of the ZIP; it's mounted on the root.
+ */
+
+ for (z = zf->topEnts; z; z = z->tnext) {
+ size_t lenz = strlen(z->name);
+
+ if ((lenz > len + 1) && (strncmp(z->name, path, len) == 0)
+ && (z->name[len] == '/')
+ && (CountSlashes(z->name) == l)
+ && Tcl_StringCaseMatch(z->name + len + 1, pattern, 0)) {
+ AppendWithPrefix(result, prefix, z->name, lenz);
+ }
+ }
+ } else if ((zf->mountPointLen > len + 1)
+ && (strncmp(zf->mountPoint, path, len) == 0)
+ && (zf->mountPoint[len] == '/')
+ && (CountSlashes(zf->mountPoint) == l)
+ && Tcl_StringCaseMatch(zf->mountPoint + len + 1,
+ pattern, 0)) {
+ /*
+ * Standard mount; append if it matches.
+ */
+
+ AppendWithPrefix(result, prefix, zf->mountPoint, zf->mountPointLen);
+ }
+ }
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
* ZipFSPathInFilesystemProc --
*
* This function determines if the given path object is in the ZIP
@@ -4267,16 +5227,14 @@ ZipFSPathInFilesystemProc(
{
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
- int ret = -1;
- size_t len;
+ int ret = -1, len;
char *path;
pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
if (!pathPtr) {
return -1;
}
-
- path = Tcl_GetStringFromObj(pathPtr, &len);
+ path = TclGetStringFromObj(pathPtr, &len);
if (strncmp(path, ZIPFS_VOLUME, ZIPFS_VOLUME_LEN) != 0) {
return -1;
}
@@ -4290,7 +5248,7 @@ ZipFSPathInFilesystemProc(
for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
hPtr = Tcl_NextHashEntry(&search)) {
- ZipFile *zf = (ZipFile *)Tcl_GetHashValue(hPtr);
+ ZipFile *zf = (ZipFile *) Tcl_GetHashValue(hPtr);
if (zf->mountPointLen == 0) {
ZipEntry *z;
@@ -4298,12 +5256,13 @@ ZipFSPathInFilesystemProc(
for (z = zf->topEnts; z != NULL; z = z->tnext) {
size_t lenz = strlen(z->name);
- if ((len >= lenz) && (strncmp(path, z->name, lenz) == 0)) {
+ if (((size_t) len >= lenz) &&
+ (strncmp(path, z->name, lenz) == 0)) {
ret = TCL_OK;
goto endloop;
}
}
- } else if ((len >= zf->mountPointLen) &&
+ } else if (((size_t) len >= zf->mountPointLen) &&
(strncmp(path, zf->mountPoint, zf->mountPointLen) == 0)) {
ret = TCL_OK;
break;
@@ -4355,11 +5314,25 @@ ZipFSListVolumesProc(void)
*-------------------------------------------------------------------------
*/
+enum ZipFileAttrs {
+ ZIP_ATTR_UNCOMPSIZE,
+ ZIP_ATTR_COMPSIZE,
+ ZIP_ATTR_OFFSET,
+ ZIP_ATTR_MOUNT,
+ ZIP_ATTR_ARCHIVE,
+ ZIP_ATTR_PERMISSIONS,
+ ZIP_ATTR_CRC
+};
+
static const char *const *
ZipFSFileAttrStringsProc(
TCL_UNUSED(Tcl_Obj *) /*pathPtr*/,
TCL_UNUSED(Tcl_Obj **) /*objPtrRef*/)
{
+ /*
+ * Must match up with ZipFileAttrs enum above.
+ */
+
static const char *const attrs[] = {
"-uncompsize",
"-compsize",
@@ -4367,6 +5340,7 @@ ZipFSFileAttrStringsProc(
"-mount",
"-archive",
"-permissions",
+ "-crc",
NULL,
};
@@ -4400,7 +5374,7 @@ ZipFSFileAttrsGetProc(
Tcl_Obj *pathPtr,
Tcl_Obj **objPtrRef)
{
- int ret = TCL_OK;
+ int len, ret = TCL_OK;
char *path;
ZipEntry *z;
@@ -4408,7 +5382,7 @@ ZipFSFileAttrsGetProc(
if (!pathPtr) {
return -1;
}
- path = TclGetString(pathPtr);
+ path = TclGetStringFromObj(pathPtr, &len);
ReadLock();
z = ZipFSLookup(path);
if (!z) {
@@ -4418,27 +5392,31 @@ ZipFSFileAttrsGetProc(
goto done;
}
switch (index) {
- case 0:
+ case ZIP_ATTR_UNCOMPSIZE:
TclNewIntObj(*objPtrRef, z->numBytes);
break;
- case 1:
+ case ZIP_ATTR_COMPSIZE:
TclNewIntObj(*objPtrRef, z->numCompressedBytes);
break;
- case 2:
+ case ZIP_ATTR_OFFSET:
TclNewIntObj(*objPtrRef, z->offset);
break;
- case 3:
+ case ZIP_ATTR_MOUNT:
*objPtrRef = Tcl_NewStringObj(z->zipFilePtr->mountPoint,
z->zipFilePtr->mountPointLen);
break;
- case 4:
+ case ZIP_ATTR_ARCHIVE:
*objPtrRef = Tcl_NewStringObj(z->zipFilePtr->name, -1);
break;
- case 5:
+ case ZIP_ATTR_PERMISSIONS:
*objPtrRef = Tcl_NewStringObj("0o555", -1);
break;
+ case ZIP_ATTR_CRC:
+ TclNewIntObj(*objPtrRef, z->crc32);
+ break;
default:
ZIPFS_ERROR(interp, "unknown attribute");
+ ZIPFS_ERROR_CODE(interp, "FILE_ATTR");
ret = TCL_ERROR;
}
@@ -4471,10 +5449,8 @@ ZipFSFileAttrsSetProc(
TCL_UNUSED(Tcl_Obj *) /*pathPtr*/,
TCL_UNUSED(Tcl_Obj *) /*objPtr*/)
{
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj("unsupported operation", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "UNSUPPORTED_OP", NULL);
- }
+ ZIPFS_ERROR(interp, "unsupported operation");
+ ZIPFS_ERROR_CODE(interp, "UNSUPPORTED_OP");
return TCL_ERROR;
}
@@ -4576,7 +5552,7 @@ ZipFSLoadFile(
if (execName) {
const char *p = strrchr(execName, '/');
- if (p > execName + 1) {
+ if (p && p > execName + 1) {
--p;
objs[0] = Tcl_NewStringObj(execName, p - execName);
}
@@ -4602,7 +5578,8 @@ ZipFSLoadFile(
Tcl_DecrRefCount(objs[1]);
}
- loadFileProc = (Tcl_FSLoadFileProc2 *)(void *)tclNativeFilesystem.loadFileProc;
+ loadFileProc = (Tcl_FSLoadFileProc2 *) (void *)
+ tclNativeFilesystem.loadFileProc;
if (loadFileProc) {
ret = loadFileProc(interp, path, loadHandle, unloadProcPtr, flags);
} else {
@@ -4691,8 +5668,12 @@ TclZipfs_Init(
Tcl_Obj *mapObj;
Tcl_EvalEx(interp, findproc, -1, TCL_EVAL_GLOBAL);
- Tcl_LinkVar(interp, "::tcl::zipfs::wrmax", (char *) &ZipFS.wrmax,
- TCL_LINK_INT);
+ if (!Tcl_IsSafe(interp)) {
+ Tcl_LinkVar(interp, "::tcl::zipfs::wrmax", (char *) &ZipFS.wrmax,
+ TCL_LINK_INT);
+ Tcl_LinkVar(interp, "::tcl::zipfs::fallbackEntryEncoding",
+ (char *) &ZipFS.fallbackEntryEncoding, TCL_LINK_STRING);
+ }
ensemble = TclMakeEnsemble(interp, "zipfs",
Tcl_IsSafe(interp) ? (initMap + 4) : initMap);
@@ -4705,12 +5686,12 @@ TclZipfs_Init(
Tcl_NewStringObj("::tcl::zipfs::find", -1));
Tcl_CreateObjCommand(interp, "::tcl::zipfs::tcl_library_init",
ZipFSTclLibraryObjCmd, NULL, NULL);
- Tcl_PkgProvideEx(interp, "tcl::zipfs", "2.0", NULL);
+ Tcl_PkgProvide(interp, "tcl::zipfs", "2.0");
}
return TCL_OK;
#else /* !HAVE_ZLIB */
ZIPFS_ERROR(interp, "no zlib available");
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "NO_ZLIB", NULL);
+ ZIPFS_ERROR_CODE(interp, "NO_ZLIB");
return TCL_ERROR;
#endif /* HAVE_ZLIB */
}
@@ -4758,7 +5739,7 @@ static void
ZipfsExitHandler(
ClientData clientData)
{
- ZipFile *zf = (ZipFile *)clientData;
+ ZipFile *zf = (ZipFile *) clientData;
if (TCL_OK != TclZipfs_Unmount(NULL, zf->mountPoint)) {
Tcl_Panic("tried to unmount busy filesystem");
@@ -4921,9 +5902,7 @@ TclZipfs_Mount(
* the ZIP is unprotected. */
{
ZIPFS_ERROR(interp, "no zlib available");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "NO_ZLIB", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "NO_ZLIB");
return TCL_ERROR;
}
@@ -4936,9 +5915,7 @@ TclZipfs_MountBuffer(
int copy)
{
ZIPFS_ERROR(interp, "no zlib available");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "NO_ZLIB", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "NO_ZLIB");
return TCL_ERROR;
}
@@ -4948,9 +5925,7 @@ TclZipfs_Unmount(
const char *mountPoint) /* Mount point path. */
{
ZIPFS_ERROR(interp, "no zlib available");
- if (interp) {
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "NO_ZLIB", NULL);
- }
+ ZIPFS_ERROR_CODE(interp, "NO_ZLIB");
return TCL_ERROR;
}
#endif /* !HAVE_ZLIB */
diff --git a/generic/tclZlib.c b/generic/tclZlib.c
index da45b2e..85b4dc3 100644
--- a/generic/tclZlib.c
+++ b/generic/tclZlib.c
@@ -3969,7 +3969,7 @@ TclZlibInit(
* Formally provide the package as a Tcl built-in.
*/
-#ifndef TCL_NO_DEPRECATED
+#if !defined(TCL_NO_DEPRECATED) && (TCL_MAJOR_VERSION < 9)
Tcl_PkgProvideEx(interp, "zlib", TCL_ZLIB_VERSION, NULL);
#endif
return Tcl_PkgProvideEx(interp, "tcl::zlib", TCL_ZLIB_VERSION, NULL);