summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
Diffstat (limited to 'generic')
-rw-r--r--generic/tcl.decls17
-rw-r--r--generic/tclCompExpr.c2
-rw-r--r--generic/tclDecls.h60
-rw-r--r--generic/tclEncoding.c4
-rw-r--r--generic/tclIO.c3
-rw-r--r--generic/tclIndexObj.c2
-rw-r--r--generic/tclInt.h13
-rw-r--r--generic/tclParse.c2
-rw-r--r--generic/tclStringObj.c8
-rw-r--r--generic/tclStubInit.c38
-rw-r--r--generic/tclTest.c5
-rw-r--r--generic/tclThreadAlloc.c2
-rw-r--r--generic/tclUtf.c67
-rw-r--r--generic/tclUtil.c6
-rw-r--r--generic/tclZipfs.c2172
-rw-r--r--generic/tclZlib.c2
16 files changed, 1436 insertions, 967 deletions
diff --git a/generic/tcl.decls b/generic/tcl.decls
index 03d43ce..11b9436a 100644
--- a/generic/tcl.decls
+++ b/generic/tcl.decls
@@ -1163,7 +1163,7 @@ declare 325 {
const char *Tcl_UtfAtIndex(const char *src, int index)
}
declare 326 {
- int Tcl_UtfCharComplete(const char *src, int length)
+ int TclUtfCharComplete(const char *src, int length)
}
declare 327 {
int Tcl_UtfBackslash(const char *src, int *readPtr, char *dst)
@@ -1175,10 +1175,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,
@@ -2412,6 +2412,17 @@ declare 652 {
declare 653 {
unsigned char *TclGetByteArrayFromObj(Tcl_Obj *objPtr, size_t *lengthPtr)
}
+
+# TIP #575
+declare 654 {
+ int Tcl_UtfCharComplete(const char *src, int length)
+}
+declare 655 {
+ const char *Tcl_UtfNext(const char *src)
+}
+declare 656 {
+ const char *Tcl_UtfPrev(const char *src, const char *start)
+}
declare 657 {
int Tcl_UniCharIsUnicode(int ch)
}
diff --git a/generic/tclCompExpr.c b/generic/tclCompExpr.c
index fa15fba..03aebe3 100644
--- a/generic/tclCompExpr.c
+++ b/generic/tclCompExpr.c
@@ -2145,7 +2145,7 @@ ParseLexeme(
*/
if (!TclIsBareword(*start) || *start == '_') {
- if (TclUCS4Complete(start, numBytes)) {
+ if (Tcl_UtfCharComplete(start, numBytes)) {
scanned = TclUtfToUCS4(start, &ch);
} else {
char utfBytes[8];
diff --git a/generic/tclDecls.h b/generic/tclDecls.h
index bb31355..ab4d8ce 100644
--- a/generic/tclDecls.h
+++ b/generic/tclDecls.h
@@ -999,7 +999,7 @@ EXTERN int Tcl_UniCharToUtf(int ch, char *buf);
/* 325 */
EXTERN const char * Tcl_UtfAtIndex(const char *src, int index);
/* 326 */
-EXTERN int Tcl_UtfCharComplete(const char *src, int length);
+EXTERN int TclUtfCharComplete(const char *src, int length);
/* 327 */
EXTERN int Tcl_UtfBackslash(const char *src, int *readPtr,
char *dst);
@@ -1008,9 +1008,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,
@@ -1931,9 +1931,12 @@ EXTERN Tcl_UniChar * TclGetUnicodeFromObj(Tcl_Obj *objPtr,
/* 653 */
EXTERN unsigned char * TclGetByteArrayFromObj(Tcl_Obj *objPtr,
size_t *lengthPtr);
-/* Slot 654 is reserved */
-/* Slot 655 is reserved */
-/* Slot 656 is reserved */
+/* 654 */
+EXTERN int Tcl_UtfCharComplete(const char *src, int length);
+/* 655 */
+EXTERN const char * Tcl_UtfNext(const char *src);
+/* 656 */
+EXTERN const char * Tcl_UtfPrev(const char *src, const char *start);
/* 657 */
EXTERN int Tcl_UniCharIsUnicode(int ch);
@@ -2297,12 +2300,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, int index); /* 325 */
- int (*tcl_UtfCharComplete) (const char *src, int length); /* 326 */
+ int (*tclUtfCharComplete) (const char *src, int length); /* 326 */
int (*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, int srcLen, int flags, Tcl_EncodingState *statePtr, char *dst, int dstLen, int *srcReadPtr, int *dstWrotePtr, int *dstCharsPtr); /* 332 */
char * (*tcl_UtfToExternalDString) (Tcl_Encoding encoding, const char *src, int srcLen, Tcl_DString *dsPtr); /* 333 */
int (*tcl_UtfToLower) (char *src); /* 334 */
@@ -2625,9 +2628,9 @@ typedef struct TclStubs {
char * (*tclGetStringFromObj) (Tcl_Obj *objPtr, size_t *lengthPtr); /* 651 */
Tcl_UniChar * (*tclGetUnicodeFromObj) (Tcl_Obj *objPtr, size_t *lengthPtr); /* 652 */
unsigned char * (*tclGetByteArrayFromObj) (Tcl_Obj *objPtr, size_t *lengthPtr); /* 653 */
- void (*reserved654)(void);
- void (*reserved655)(void);
- void (*reserved656)(void);
+ int (*tcl_UtfCharComplete) (const char *src, int length); /* 654 */
+ const char * (*tcl_UtfNext) (const char *src); /* 655 */
+ const char * (*tcl_UtfPrev) (const char *src, const char *start); /* 656 */
int (*tcl_UniCharIsUnicode) (int ch); /* 657 */
} TclStubs;
@@ -3311,18 +3314,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 \
@@ -3965,9 +3968,12 @@ extern const TclStubs *tclStubsPtr;
(tclStubsPtr->tclGetUnicodeFromObj) /* 652 */
#define TclGetByteArrayFromObj \
(tclStubsPtr->tclGetByteArrayFromObj) /* 653 */
-/* Slot 654 is reserved */
-/* Slot 655 is reserved */
-/* Slot 656 is reserved */
+#define Tcl_UtfCharComplete \
+ (tclStubsPtr->tcl_UtfCharComplete) /* 654 */
+#define Tcl_UtfNext \
+ (tclStubsPtr->tcl_UtfNext) /* 655 */
+#define Tcl_UtfPrev \
+ (tclStubsPtr->tcl_UtfPrev) /* 656 */
#define Tcl_UniCharIsUnicode \
(tclStubsPtr->tcl_UniCharIsUnicode) /* 657 */
@@ -4245,10 +4251,16 @@ extern const TclStubs *tclStubsPtr;
#define Tcl_Close(interp, chan) Tcl_CloseEx(interp, chan, 0)
#endif
-#if defined(USE_TCL_STUBS) && (TCL_UTF_MAX > 3)
+#undef TclUtfCharComplete
+#undef TclUtfNext
+#undef TclUtfPrev
+#if defined(USE_TCL_STUBS) && (TCL_UTF_MAX < 4) && !defined(TCL_NO_DEPRECATED)
# undef Tcl_UtfCharComplete
-# define Tcl_UtfCharComplete(src, length) (((unsigned)((unsigned char)*(src) - 0xF0) < 5) \
- ? ((length) >= 4) : tclStubsPtr->tcl_UtfCharComplete((src), (length)))
+# undef Tcl_UtfNext
+# undef Tcl_UtfPrev
+# define Tcl_UtfCharComplete (tclStubsPtr->tclUtfCharComplete)
+# define Tcl_UtfNext (tclStubsPtr->tclUtfNext)
+# define Tcl_UtfPrev (tclStubsPtr->tclUtfPrev)
#endif
#define Tcl_CreateSlave Tcl_CreateChild
#define Tcl_GetSlave Tcl_GetChild
diff --git a/generic/tclEncoding.c b/generic/tclEncoding.c
index d994f10..2198c33 100644
--- a/generic/tclEncoding.c
+++ b/generic/tclEncoding.c
@@ -2330,7 +2330,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.
@@ -2360,7 +2360,7 @@ UtfToUtfProc(
*dst++ = 0;
*chPtr = 0; /* reset surrogate handling */
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
diff --git a/generic/tclIO.c b/generic/tclIO.c
index 9cace8c..3954af2 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -3387,7 +3387,8 @@ int
Tcl_Close(
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/tclIndexObj.c b/generic/tclIndexObj.c
index f08278b..30d6cbd 100644
--- a/generic/tclIndexObj.c
+++ b/generic/tclIndexObj.c
@@ -785,7 +785,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;
}
diff --git a/generic/tclInt.h b/generic/tclInt.h
index cc4f6a9..1d192ff 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -3253,16 +3253,10 @@ MODULE_SCOPE int 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(ClientData clientData);
MODULE_SCOPE Tcl_Obj * TclpFilesystemPathType(Tcl_Obj *pathPtr);
@@ -4696,11 +4690,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
@@ -4737,7 +4726,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))
#else /* !WORDS_BIGENDIAN */
# define TclUniCharNcmp Tcl_UniCharNcmp
diff --git a/generic/tclParse.c b/generic/tclParse.c
index df6c9bf..4de0356 100644
--- a/generic/tclParse.c
+++ b/generic/tclParse.c
@@ -935,7 +935,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/tclStringObj.c b/generic/tclStringObj.c
index 0777018..b557af0 100644
--- a/generic/tclStringObj.c
+++ b/generic/tclStringObj.c
@@ -1199,10 +1199,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;
}
/*
@@ -2644,7 +2644,7 @@ AppendPrintfToObjVA(
* multi-byte characters.
*/
- q = TclUtfPrev(end, bytes);
+ q = Tcl_UtfPrev(end, bytes);
if (!Tcl_UtfCharComplete(q, (int)(end - q))) {
end = q;
}
@@ -3456,7 +3456,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 cd1ce81..8b5dd89 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -90,6 +90,32 @@ static void uniCodePanic(void) {
# define Tcl_UniCharNcmp (int(*)(const Tcl_UniChar *, const Tcl_UniChar *, unsigned long))(void *)uniCodePanic
#endif
+#define TclUtfCharComplete UtfCharComplete
+#define TclUtfNext UtfNext
+#define TclUtfPrev UtfPrev
+
+static int TclUtfCharComplete(const char *src, int length) {
+ if ((unsigned)((unsigned char)*(src) - 0xF0) < 5) {
+ return length < 3;
+ }
+ return Tcl_UtfCharComplete(src, length);
+}
+
+static const char *TclUtfNext(const char *src) {
+ if ((unsigned)((unsigned char)*(src) - 0xF0) < 5) {
+ return src + 1;
+ }
+ return Tcl_UtfNext(src);
+}
+
+static const char *TclUtfPrev(const char *src, const char *start) {
+ if ((src >= start + 3) && ((src[-1] & 0xC0) == 0x80)
+ && ((src[-2] & 0xC0) == 0x80) && ((src[-3] & 0xC0) == 0x80)) {
+ return src - 3;
+ }
+ return Tcl_UtfPrev(src, start);
+}
+
#define TclBN_mp_add mp_add
#define TclBN_mp_and mp_and
#define TclBN_mp_clamp mp_clamp
@@ -1575,12 +1601,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 */
@@ -1903,9 +1929,9 @@ const TclStubs tclStubs = {
TclGetStringFromObj, /* 651 */
TclGetUnicodeFromObj, /* 652 */
TclGetByteArrayFromObj, /* 653 */
- 0, /* 654 */
- 0, /* 655 */
- 0, /* 656 */
+ Tcl_UtfCharComplete, /* 654 */
+ Tcl_UtfNext, /* 655 */
+ Tcl_UtfPrev, /* 656 */
Tcl_UniCharIsUnicode, /* 657 */
};
diff --git a/generic/tclTest.c b/generic/tclTest.c
index 45b5ca3..1c35ab2 100644
--- a/generic/tclTest.c
+++ b/generic/tclTest.c
@@ -19,6 +19,9 @@
#ifndef USE_TCL_STUBS
# define USE_TCL_STUBS
#endif
+#ifndef TCL_NO_DEPRECATED
+# define TCL_NO_DEPRECATED
+#endif
#include "tclInt.h"
#ifdef TCL_WITH_EXTERNAL_TOMMATH
# include "tommath.h"
@@ -6962,7 +6965,7 @@ TestUtfPrevCmd(
} else {
offset = numBytes;
}
- result = TclUtfPrev(bytes + offset, bytes);
+ result = Tcl_UtfPrev(bytes + offset, bytes);
Tcl_SetObjResult(interp, Tcl_NewIntObj(result - bytes));
return TCL_OK;
}
diff --git a/generic/tclThreadAlloc.c b/generic/tclThreadAlloc.c
index 28475f9..727f061 100644
--- a/generic/tclThreadAlloc.c
+++ b/generic/tclThreadAlloc.c
@@ -564,7 +564,7 @@ TclThreadAllocObj(void)
cachePtr->numObjects = numMove = NOBJALLOC;
newObjsPtr = (Tcl_Obj *)TclpSysAlloc(sizeof(Tcl_Obj) * numMove, 0);
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/tclUtf.c b/generic/tclUtf.c
index 2687a1d..fcdf80a 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,17 +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
- /* Tcl_UtfToUniChar() accesses src[1] and src[2] to check whether
- * the UTF-8 sequence is valid, so we cannot use 1 here. */
- 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.
*/
@@ -696,7 +680,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;
}
@@ -754,7 +738,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 {
@@ -835,7 +819,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...
@@ -1070,7 +1054,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
@@ -1111,19 +1095,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
}
/*
@@ -1750,7 +1729,7 @@ Tcl_UniCharToLower(
/* Clear away extension bits, if any */
return ch & 0x1FFFFF;
}
-
+
/*
*----------------------------------------------------------------------
*
@@ -1844,7 +1823,7 @@ Tcl_UniCharNcmp(
const Tcl_UniChar *uct, /* Unicode string ucs is compared to. */
unsigned long 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
*/
@@ -1858,6 +1837,14 @@ Tcl_UniCharNcmp(
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);
}
}
@@ -1895,6 +1882,14 @@ Tcl_UniCharNcasecmp(
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 1904e2f..d5ec040 100644
--- a/generic/tclUtil.c
+++ b/generic/tclUtil.c
@@ -1707,11 +1707,7 @@ TclTrimRight(
const char *q = trim;
int 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 b8a772b..ce06a8e 100644
--- a/generic/tclZipfs.c
+++ b/generic/tclZipfs.c
@@ -129,6 +129,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,28 +144,11 @@
"%s: %s", errstr, Tcl_PosixError(interp))); \
} \
} while (0)
-
-/*
- * Macros to read and write little-endial 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)
/*
@@ -178,6 +169,12 @@ TCL_DECLARE_MUTEX(localtimeMutex)
#endif /* !_WIN32 && !HAVE_LOCALTIME_R && TCL_THREADS */
/*
+ * Forward declaration.
+ */
+
+struct ZipEntry;
+
+/*
* In-core description of mounted ZIP archive file.
*/
@@ -206,6 +203,7 @@ typedef struct ZipFile {
/*
* In-core description of file contained in mounted ZIP archive.
+ * ZIP_ATTR_
*/
typedef struct ZipEntry {
@@ -261,7 +259,9 @@ 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. */
int idCount; /* Counter for channel names */
Tcl_HashTable fileHash; /* File name to ZipEntry mapping */
Tcl_HashTable zipHash; /* Mount to ZipFile mapping */
@@ -287,15 +287,26 @@ 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(char *buf, ZipEntry *z,
- size_t nameLength, long long dataStartOffset);
-static void SerializeCentralDirectorySuffix(char *buf,
+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(char *buf, ZipEntry *z,
- int nameLength, int align);
+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
@@ -310,6 +321,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);
@@ -329,7 +343,7 @@ static int ZipChannelClose(void *instanceData,
static Tcl_DriverGetHandleProc ZipChannelGetFile;
static int ZipChannelRead(void *instanceData, char *buf,
int toRead, int *errloc);
-#ifndef TCL_NO_DEPRECATED
+#if !defined(TCL_NO_DEPRECATED) && (TCL_MAJOR_VERSION < 9)
static int ZipChannelSeek(void *instanceData, long offset,
int mode, int *errloc);
#endif
@@ -383,27 +397,28 @@ static const Tcl_Filesystem zipfsFilesystem = {
*/
static Tcl_ChannelType ZipChannelType = {
- "zip", /* Type name. */
+ "zip", /* Type name. */
TCL_CHANNEL_VERSION_5,
- TCL_CLOSE2PROC, /* Close channel, clean instance data */
- ZipChannelRead, /* Handle read request */
- ZipChannelWrite, /* Handle write request */
-#ifndef TCL_NO_DEPRECATED
- ZipChannelSeek, /* Move location of access point, 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 */
+ 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 */
+ 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 */
};
/*
@@ -415,6 +430,79 @@ static Tcl_ChannelType ZipChannelType = {
/*
*-------------------------------------------------------------------------
*
+ * 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;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
* ReadLock, WriteLock, Unlock --
*
* POSIX like rwlock functions to support multiple readers and single
@@ -433,7 +521,7 @@ TCL_DECLARE_MUTEX(ZipFSMutex)
static Tcl_Condition ZipFSCond;
-static void
+static inline void
ReadLock(void)
{
Tcl_MutexLock(&ZipFSMutex);
@@ -446,7 +534,7 @@ ReadLock(void)
Tcl_MutexUnlock(&ZipFSMutex);
}
-static void
+static inline void
WriteLock(void)
{
Tcl_MutexLock(&ZipFSMutex);
@@ -459,7 +547,7 @@ WriteLock(void)
Tcl_MutexUnlock(&ZipFSMutex);
}
-static void
+static inline void
Unlock(void)
{
Tcl_MutexLock(&ZipFSMutex);
@@ -579,7 +667,7 @@ ToDosDate(
*-------------------------------------------------------------------------
*/
-static int
+static inline int
CountSlashes(
const char *string)
{
@@ -783,9 +871,9 @@ CanonicalPath(
*-------------------------------------------------------------------------
*/
-static ZipEntry *
+static inline ZipEntry *
ZipFSLookup(
- char *filename)
+ const char *filename)
{
Tcl_HashEntry *hPtr;
ZipEntry *z = NULL;
@@ -800,13 +888,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.
@@ -814,45 +902,40 @@ ZipFSLookup(
*-------------------------------------------------------------------------
*/
-#ifdef NEVER_USED
-static int
-ZipFSLookupMount(
- char *filename)
+static inline ZipFile *
+ZipFSLookupZip(
+ const char *mountPoint)
{
Tcl_HashEntry *hPtr;
- Tcl_HashSearch search;
-
- for (hPtr = Tcl_FirstHashEntry(&ZipFS.zipHash, &search); hPtr;
- hPtr = Tcl_NextHashEntry(&search)) {
- ZipFile *zf = Tcl_GetHashValue(hPtr);
+ ZipFile *zf = NULL;
- if (strcmp(zf->mountPoint, filename) == 0) {
- return 1;
- }
+ hPtr = Tcl_FindHashEntry(&ZipFS.zipHash, mountPoint);
+ if (hPtr) {
+ zf = (ZipFile *) Tcl_GetHashValue(hPtr);
}
- return 0;
+ return zf;
}
-#endif /* NEVER_USED */
/*
*-------------------------------------------------------------------------
*
- * AllocateZipFile --
+ * AllocateZipFile, AllocateZipEntry, AllocateZipChannel --
*
- * Allocates the memory for a ZipFile structure. Always ensures that it
- * is zeroed out for safety.
+ * 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 in
- * a low-memory situation.
+ * The interpreter result may be written to on error. Which might fail
+ * (for ZipFile) in a low-memory situation. Always panics if ZipEntry
+ * allocation fails.
*
*-------------------------------------------------------------------------
*/
-static ZipFile *
+static inline ZipFile *
AllocateZipFile(
Tcl_Interp *interp,
size_t mountPointNameLength)
@@ -861,15 +944,34 @@ AllocateZipFile(
ZipFile *zf = (ZipFile *) attemptckalloc(size);
if (!zf) {
- if (interp) {
- Tcl_AppendResult(interp, "out of memory", (char *) NULL);
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
+ ZIPFS_MEM_ERROR(interp);
} else {
memset(zf, 0, size);
}
return zf;
}
+
+static inline ZipEntry *
+AllocateZipEntry(void)
+{
+ ZipEntry *z = (ZipEntry *) ckalloc(sizeof(ZipEntry));
+ memset(z, 0, sizeof(ZipEntry));
+ return z;
+}
+
+static inline ZipChannel *
+AllocateZipChannel(
+ Tcl_Interp *interp)
+{
+ ZipChannel *zc = (ZipChannel *) attemptckalloc(sizeof(ZipChannel));
+
+ if (!zc) {
+ ZIPFS_MEM_ERROR(interp);
+ } else {
+ memset(zc, 0, sizeof(ZipChannel));
+ }
+ return zc;
+}
/*
*-------------------------------------------------------------------------
@@ -962,7 +1064,9 @@ 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
@@ -971,9 +1075,9 @@ ZipFSFindTOC(
*/
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;
@@ -992,9 +1096,7 @@ ZipFSFindTOC(
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;
}
@@ -1002,16 +1104,14 @@ ZipFSFindTOC(
* How many files in the archive? If that's bogus, we're done here.
*/
- zf->numFiles = ZipReadShort(p + ZIP_CENTRAL_ENTS_OFFS);
+ 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;
}
@@ -1019,18 +1119,16 @@ ZipFSFindTOC(
* Where does the central directory start?
*/
- 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)
+ 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;
}
@@ -1044,23 +1142,19 @@ ZipFSFindTOC(
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;
}
@@ -1070,11 +1164,15 @@ ZipFSFindTOC(
*/
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;
}
}
@@ -1167,9 +1265,7 @@ ZipFSOpenArchive(
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) {
@@ -1178,10 +1274,7 @@ ZipFSOpenArchive(
}
zf->ptrToFree = zf->data = (unsigned char *) attemptckalloc(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);
@@ -1283,16 +1376,45 @@ ZipMapArchive(
/*
*-------------------------------------------------------------------------
*
- * ZipFSRootNode --
+ * 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;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * 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.
*
*-------------------------------------------------------------------------
*/
@@ -1321,12 +1443,7 @@ 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;
}
}
@@ -1335,13 +1452,10 @@ ZipFSCatalogFilesystem(
* Validate the TOC data. If that's bad, things fall apart.
*/
- if (zf0->baseOffset < 0 || zf0->baseOffset >= zf0->length ||
- zf0->passOffset < 0 || zf0->passOffset >= zf0->length ||
- zf0->directoryOffset < 0 || zf0->directoryOffset >= zf0->length) {
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj("bad zip data", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "BAD_ZIP", NULL);
- }
+ 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;
}
@@ -1365,7 +1479,7 @@ ZipFSCatalogFilesystem(
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);
@@ -1387,12 +1501,11 @@ ZipFSCatalogFilesystem(
zf->mountPoint = (char *) Tcl_GetHashKey(&ZipFS.zipHash, hPtr);
Tcl_CreateExitHandler(ZipfsExitHandler, zf);
zf->mountPointLen = strlen(zf->mountPoint);
+
zf->nameLength = strlen(zipname);
zf->name = (char *) ckalloc(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;
@@ -1407,20 +1520,14 @@ ZipFSCatalogFilesystem(
if (mountPoint[0] != '\0') {
hPtr = Tcl_CreateHashEntry(&ZipFS.fileHash, mountPoint, &isNew);
if (isNew) {
- z = (ZipEntry *) ckalloc(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->next = zf->entries;
zf->entries = z;
@@ -1429,14 +1536,16 @@ 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);
+ pathlen = ZipReadShort(start, end, q + ZIP_CENTRAL_PATHLEN_OFFS);
+ comlen = ZipReadShort(start, end, q + ZIP_CENTRAL_FCOMMENTLEN_OFFS);
+ extra = ZipReadShort(start, end, q + ZIP_CENTRAL_EXTRALEN_OFFS);
Tcl_DStringSetLength(&ds, 0);
Tcl_DStringAppend(&ds, (char *) q + ZIP_CENTRAL_HEADER_LEN, pathlen);
path = Tcl_DStringValue(&ds);
@@ -1449,24 +1558,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
/*
@@ -1482,8 +1592,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;
@@ -1500,83 +1609,91 @@ ZipFSCatalogFilesystem(
goto nextent;
#endif /* ANDROID */
}
+
Tcl_DStringSetLength(&fpBuf, 0);
fullpath = CanonicalPath(mountPoint, path, &fpBuf, 1);
- z = (ZipEntry *) ckalloc(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 */
ckfree(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 *) ckalloc(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;
}
}
}
@@ -1650,17 +1767,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);
+ 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;
}
/*
@@ -1687,13 +1815,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;
}
@@ -1761,15 +1886,8 @@ 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 = AllocateZipFile(interp, strlen(mountPoint));
if (!zf) {
@@ -1857,10 +1975,7 @@ TclZipfs_MountBuffer(
if (copy) {
zf->data = (unsigned char *) attemptckalloc(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);
@@ -1869,7 +1984,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;
}
@@ -1928,10 +2042,17 @@ TclZipfs_Unmount(
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);
@@ -1947,6 +2068,7 @@ TclZipfs_Unmount(
Tcl_DeleteExitHandler(ZipfsExitHandler, zf);
ckfree(zf);
unmounted = 1;
+
done:
Unlock();
if (unmounted) {
@@ -2117,7 +2239,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;
@@ -2150,37 +2271,80 @@ 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 = Tcl_GetString(objv[1]);
- len = strlen(pw);
+ pw = Tcl_GetStringFromObj(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;
}
/*
@@ -2188,7 +2352,7 @@ ZipFSMkKeyObjCmd(
*
* 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.
@@ -2206,15 +2370,18 @@ 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. */
+ 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;
@@ -2243,11 +2410,11 @@ ZipAddFile(
zpathlen = strlen(zpath);
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\"", Tcl_GetString(pathObj)));
+ ZIPFS_ERROR_CODE(interp, "PATH_LEN");
return TCL_ERROR;
}
- in = Tcl_OpenFileChannel(interp, path, "rb", 0);
+ in = Tcl_FSOpenFileChannel(interp, pathObj, "rb", 0);
if (!in) {
#ifdef _WIN32
/* hopefully a directory */
@@ -2259,16 +2426,18 @@ ZipAddFile(
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) {
@@ -2278,8 +2447,9 @@ ZipAddFile(
Tcl_Close(interp, in);
return TCL_OK;
}
+ readErrorWithChannelOpen:
Tcl_SetObjResult(interp, Tcl_ObjPrintf("read error on \"%s\": %s",
- path, Tcl_PosixError(interp)));
+ Tcl_GetString(pathObj), Tcl_PosixError(interp)));
Tcl_Close(interp, in);
return TCL_ERROR;
}
@@ -2291,66 +2461,68 @@ ZipAddFile(
}
if (Tcl_Seek(in, 0, SEEK_SET) == -1) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("seek error on \"%s\": %s",
- path, Tcl_PosixError(interp)));
+ Tcl_GetString(pathObj), Tcl_PosixError(interp)));
Tcl_Close(interp, in);
return TCL_ERROR;
}
+
+ /*
+ * Remember where we've got to so far so we can write the header (after
+ * writing the file).
+ */
+
headerStartOffset = Tcl_Tell(out);
+
+ /*
+ * Reserve space for the per-file header. Includes writing the file name
+ * as we already know that.
+ */
+
memset(buf, '\0', ZIP_LOCAL_HEADER_LEN);
memcpy(buf + ZIP_LOCAL_HEADER_LEN, zpath, zpathlen);
len = zpathlen + ZIP_LOCAL_HEADER_LEN;
if ((size_t) Tcl_Write(out, buf, len) != len) {
- wrerr:
+ writeErrorWithChannelOpen:
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error on %s: %s", path, Tcl_PosixError(interp)));
+ "write error on \"%s\": %s",
+ Tcl_GetString(pathObj), Tcl_PosixError(interp)));
Tcl_Close(interp, in);
return TCL_ERROR;
}
+
+ /*
+ * Align payload to next 4-byte boundary (if necessary) using a dummy
+ * extra entry similar to the zipalign tool from Android's SDK.
+ */
+
if ((len + headerStartOffset) & 3) {
unsigned char abuf[8];
-
- /*
- * Align payload to next 4-byte boundary using a dummy extra entry
- * similar to the zipalign tool from Android's SDK.
- */
+ const unsigned char *astart = abuf;
+ const unsigned char *aend = abuf + 8;
align = 4 + ((len + headerStartOffset) & 3);
- ZipWriteShort(abuf, 0xffff);
- ZipWriteShort(abuf + 2, align - 4);
- ZipWriteInt(abuf + 4, 0x03020100);
+ 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 wrerr;
+ 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_Close(interp, in);
- 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);
+ 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);
@@ -2363,16 +2535,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_Close(interp, in);
- 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);
dataStartOffset = Tcl_Tell(out);
+
+ /*
+ * Compress the stream.
+ */
+
compMeth = ZIP_COMPMETH_DEFLATED;
memset(&stream, 0, sizeof(z_stream));
stream.zalloc = Z_NULL;
@@ -2381,19 +2560,17 @@ 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);
+ "compression init error on \"%s\"", Tcl_GetString(pathObj)));
+ ZIPFS_ERROR_CODE(interp, "DEFLATE_INIT");
Tcl_Close(interp, in);
return TCL_ERROR;
}
+
do {
len = Tcl_Read(in, buf, bufsize);
if (len == ERROR_LENGTH) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "read error on %s: %s", path, Tcl_PosixError(interp)));
deflateEnd(&stream);
- Tcl_Close(interp, in);
- return TCL_ERROR;
+ goto readErrorWithChannelOpen;
}
stream.avail_in = len;
stream.next_in = (unsigned char *) buf;
@@ -2404,8 +2581,8 @@ 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\"", Tcl_GetString(pathObj)));
+ ZIPFS_ERROR_CODE(interp, "DEFLATE");
deflateEnd(&stream);
Tcl_Close(interp, in);
return TCL_ERROR;
@@ -2420,18 +2597,21 @@ ZipAddFile(
}
}
if (olen && ((size_t) Tcl_Write(out, obuf, olen) != olen)) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error: %s", Tcl_PosixError(interp)));
deflateEnd(&stream);
- Tcl_Close(interp, in);
- 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);
dataEndOffset = Tcl_Tell(out);
+
if (nbyte - nbytecompr <= 0) {
/*
* Compressed file larger than input, write it again uncompressed.
@@ -2442,20 +2622,16 @@ ZipAddFile(
}
if (Tcl_Seek(out, dataStartOffset, SEEK_SET) != dataStartOffset) {
seekErr:
- Tcl_Close(interp, in);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"seek error: %s", Tcl_PosixError(interp)));
+ Tcl_Close(interp, in);
return TCL_ERROR;
}
nbytecompr = (passwd ? 12 : 0);
while (1) {
len = Tcl_Read(in, buf, bufsize);
if (len == ERROR_LENGTH) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "read error on \"%s\": %s",
- path, Tcl_PosixError(interp)));
- Tcl_Close(interp, in);
- return TCL_ERROR;
+ goto readErrorWithChannelOpen;
} else if (len == 0) {
break;
}
@@ -2468,14 +2644,17 @@ ZipAddFile(
}
}
if ((size_t) Tcl_Write(out, buf, len) != len) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "write error: %s", Tcl_PosixError(interp)));
- Tcl_Close(interp, in);
- return TCL_ERROR;
+ goto writeErrorWithChannelOpen;
}
nbytecompr += len;
}
compMeth = ZIP_COMPMETH_STORED;
+
+ /*
+ * Chop off everything after this; it's the over-large compressed data
+ * and we don't know if it is going to get overwritten otherwise.
+ */
+
Tcl_Flush(out);
dataEndOffset = Tcl_Tell(out);
Tcl_TruncateChannel(out, dataEndOffset);
@@ -2485,18 +2664,19 @@ ZipAddFile(
hPtr = Tcl_CreateHashEntry(fileHash, zpath, &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\"", Tcl_GetString(pathObj)));
+ ZIPFS_ERROR_CODE(interp, "DUPLICATE_PATH");
return TCL_ERROR;
}
- z = (ZipEntry *) ckalloc(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 = headerStartOffset;
z->crc32 = crc;
@@ -2504,15 +2684,14 @@ ZipAddFile(
z->numBytes = nbyte;
z->numCompressedBytes = nbytecompr;
z->compressMethod = compMeth;
- z->data = NULL;
z->name = (char *) Tcl_GetHashKey(fileHash, hPtr);
- z->next = NULL;
/*
* Write final local header information.
*/
- SerializeLocalEntryHeader(buf, z, zpathlen, align);
+ SerializeLocalEntryHeader(start, end, (unsigned char *) buf, z,
+ zpathlen, align);
if (Tcl_Seek(out, headerStartOffset, SEEK_SET) != headerStartOffset) {
Tcl_DeleteHashEntry(hPtr);
ckfree(z);
@@ -2541,12 +2720,96 @@ 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, 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 */
+ int slen) /* The length of the prefix */
+{
+ const char *name;
+ int len;
+
+ if (directNameObj) {
+ name = Tcl_GetString(directNameObj);
+ } else {
+ name = Tcl_GetStringFromObj(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].
*
* Results:
* A standard Tcl result.
@@ -2558,15 +2821,24 @@ ZipAddFile(
*/
static int
-ZipFSMkZipOrImgObjCmd(
+ZipFSMkZipOrImg(
Tcl_Interp *interp, /* Current interpreter. */
- int isImg,
- Tcl_Obj *targetFile,
- Tcl_Obj *dirRoot,
- Tcl_Obj *mappingList,
- Tcl_Obj *originFile,
- Tcl_Obj *stripPrefix,
- Tcl_Obj *passwordObj)
+ 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, slen = 0, count, ret = TCL_ERROR, lobjc;
@@ -2585,6 +2857,8 @@ ZipFSMkZipOrImgObjCmd(
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.
@@ -2593,30 +2867,19 @@ ZipFSMkZipOrImgObjCmd(
passBuf[0] = 0;
if (passwordObj != NULL) {
pw = Tcl_GetStringFromObj(passwordObj, &pwlen);
- if ((pwlen > 255) || strchr(pw, 0xff)) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj("illegal password", -1));
- Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "BAD_PASS", NULL);
+ if (IsPasswordValid(interp, pw, pwlen) != TCL_OK) {
return TCL_ERROR;
}
+ if (pwlen <= 0) {
+ pw = NULL;
+ pwlen = 0;
+ }
}
if (dirRoot != NULL) {
- Tcl_Obj *cmd[2];
- int result;
-
- /*
- * Discover the list of files to add.
- */
-
- 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) {
+ list = ZipFSFind(interp, dirRoot);
+ if (!list) {
return TCL_ERROR;
}
- list = Tcl_GetObjResult(interp);
}
Tcl_IncrRefCount(list);
if (Tcl_ListObjGetElements(interp, list, &lobjc, &lobjv) != TCL_OK) {
@@ -2625,26 +2888,21 @@ ZipFSMkZipOrImgObjCmd(
}
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, Tcl_GetString(targetFile), "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.
@@ -2767,29 +3025,14 @@ ZipFSMkZipOrImgObjCmd(
strip = Tcl_GetStringFromObj(stripPrefix, &slen);
}
for (i = 0; i < (size_t) lobjc; i += (mappingList ? 2 : 1)) {
- const char *path, *name;
+ Tcl_Obj *pathObj = lobjv[i];
+ const char *name = ComputeNameInArchive(pathObj,
+ (mappingList ? lobjv[i + 1] : NULL), strip, slen);
- path = Tcl_GetString(lobjv[i]);
- if (mappingList) {
- name = Tcl_GetString(lobjv[i + 1]);
- } else {
- name = path;
- if (slen > 0) {
- len = strlen(name);
- if ((len <= (size_t) 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;
}
@@ -2802,25 +3045,9 @@ ZipFSMkZipOrImgObjCmd(
directoryStartOffset = Tcl_Tell(out);
count = 0;
for (i = 0; i < (size_t) lobjc; i += (mappingList ? 2 : 1)) {
- const char *path, *name;
+ const char *name = ComputeNameInArchive(lobjv[i],
+ (mappingList ? lobjv[i + 1] : NULL), strip, slen);
- path = Tcl_GetString(lobjv[i]);
- if (mappingList) {
- name = Tcl_GetString(lobjv[i + 1]);
- } else {
- name = path;
- if (slen > 0) {
- len = strlen(name);
- if ((len <= (size_t) slen) ||
- (strncmp(strip, name, slen) != 0)) {
- continue;
- }
- name += slen;
- }
- }
- while (name[0] == '/') {
- ++name;
- }
if (name[0] == '\0') {
continue;
}
@@ -2830,7 +3057,8 @@ ZipFSMkZipOrImgObjCmd(
}
z = (ZipEntry *) Tcl_GetHashValue(hPtr);
len = strlen(z->name);
- SerializeCentralDirectoryEntry(buf, z, len, dataStartOffset);
+ 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, z->name, len) != len)) {
@@ -2847,8 +3075,8 @@ ZipFSMkZipOrImgObjCmd(
Tcl_Flush(out);
suffixStartOffset = Tcl_Tell(out);
- SerializeCentralDirectorySuffix(buf, count, dataStartOffset,
- directoryStartOffset, suffixStartOffset);
+ 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)));
@@ -2879,8 +3107,8 @@ ZipFSMkZipOrImgObjCmd(
*
* CopyImageFile --
*
- * A simple file copy function that is used (by ZipFSMkZipOrImgObjCmd)
- * for anything that is not an image with a ZIP appended.
+ * 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.
@@ -2975,54 +3203,71 @@ CopyImageFile(
static void
SerializeLocalEntryHeader(
- char *buf, /* Where to serialize to */
+ 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(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, nameLength);
- ZipWriteShort(buf + ZIP_LOCAL_EXTRALEN_OFFS, align);
+ 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(
- char *buf, /* Where to serialize to */
+ 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(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, nameLength);
- 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 - dataStartOffset);
+ 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(
- char *buf, /* Where to serialize to */
+ 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. */
@@ -3033,16 +3278,17 @@ SerializeCentralDirectorySuffix(
* suffix of the central directory (i.e.,
* where this data will be written). */
{
- 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, entryCount);
- ZipWriteShort(buf + ZIP_CENTRAL_TOTALENTS_OFFS, entryCount);
- ZipWriteInt(buf + ZIP_CENTRAL_DIRSIZE_OFFS,
+ 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(buf + ZIP_CENTRAL_DIRSTART_OFFS,
+ ZipWriteInt(start, end, buf + ZIP_CENTRAL_DIRSTART_OFFS,
directoryStartOffset - dataStartOffset);
- ZipWriteShort(buf + ZIP_CENTRAL_COMMENTLEN_OFFS, 0);
+ ZipWriteShort(start, end, buf + ZIP_CENTRAL_COMMENTLEN_OFFS, 0);
}
/*
@@ -3051,13 +3297,13 @@ SerializeCentralDirectorySuffix(
* 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().
*
*-------------------------------------------------------------------------
*/
@@ -3076,15 +3322,14 @@ ZipFSMkZipObjCmd(
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;
}
stripPrefix = (objc > 3 ? objv[3] : NULL);
password = (objc > 4 ? objv[4] : NULL);
- return ZipFSMkZipOrImgObjCmd(interp, 0, objv[1], objv[2], NULL, NULL,
+ return ZipFSMkZipOrImg(interp, 0, objv[1], objv[2], NULL, NULL,
stripPrefix, password);
}
@@ -3102,14 +3347,13 @@ ZipFSLMkZipObjCmd(
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;
}
password = (objc > 3 ? objv[3] : NULL);
- return ZipFSMkZipOrImgObjCmd(interp, 0, objv[1], NULL, objv[2], NULL,
+ return ZipFSMkZipOrImg(interp, 0, objv[1], NULL, objv[2], NULL,
NULL, password);
}
@@ -3119,13 +3363,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().
*
*-------------------------------------------------------------------------
*/
@@ -3145,16 +3389,15 @@ ZipFSMkImgObjCmd(
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;
}
originFile = (objc > 5 ? objv[5] : NULL);
stripPrefix = (objc > 3 ? objv[3] : NULL);
password = (objc > 4 ? objv[4] : NULL);
- return ZipFSMkZipOrImgObjCmd(interp, 1, objv[1], objv[2], NULL,
+ return ZipFSMkZipOrImg(interp, 1, objv[1], objv[2], NULL,
originFile, stripPrefix, password);
}
@@ -3172,15 +3415,14 @@ ZipFSLMkImgObjCmd(
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;
}
originFile = (objc > 4 ? objv[4] : NULL);
password = (objc > 3 ? objv[3] : NULL);
- return ZipFSMkZipOrImgObjCmd(interp, 1, objv[1], NULL, objv[2],
+ return ZipFSMkZipOrImg(interp, 1, objv[1], NULL, objv[2],
originFile, NULL, password);
}
@@ -3296,7 +3538,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.
@@ -3372,31 +3614,43 @@ 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) {
- int 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 = Tcl_GetString(objv[2]);
- } else if ((n >= 2) && (strncmp(what, "-regexp", n) == 0)) {
+ break;
+ case OPT_REGEXP:
regexp = Tcl_RegExpCompile(interp, Tcl_GetString(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 = Tcl_GetString(objv[1]);
}
+
+ /*
+ * Scan for matching entries.
+ */
+
ReadLock();
if (pattern) {
for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
@@ -3814,7 +4068,7 @@ ZipChannelWideSeek(
return info->numRead;
}
-#ifndef TCL_NO_DEPRECATED
+#if !defined(TCL_NO_DEPRECATED) && (TCL_MAJOR_VERSION < 9)
static int
ZipChannelSeek(
void *instanceData,
@@ -3883,7 +4137,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.
@@ -3897,24 +4151,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) {
@@ -3926,188 +4175,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 *) attemptckalloc(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 *) attemptckalloc(info->maxWrite);
- if (!info->ubuf) {
- merror0:
- if (info->ubuf) {
- ckfree(info->ubuf);
- }
+ if (InitWritableChannel(interp, info, z, trunc) == TCL_ERROR) {
ckfree(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) {
- unsigned int 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) {
- unsigned int j;
-
- stream.avail_in -= 12;
- cbuf = (unsigned char *) attemptckalloc(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));
- ckfree(cbuf);
- }
- goto wrapchan;
- }
- cerror0:
- if (cbuf) {
- memset(info->keys, 0, sizeof(info->keys));
- ckfree(cbuf);
- }
- if (info->ubuf) {
- ckfree(info->ubuf);
- }
- ckfree(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) {
+ ckfree(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 *) attemptckalloc(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];
@@ -4122,119 +4344,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;
- unsigned int 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 *) attemptckalloc(stream.avail_in);
- if (!ubuf) {
- info->ubuf = NULL;
- goto merror;
+ cbuf = (unsigned char *) attemptckalloc(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 *)
- attemptckalloc(info->numBytes);
- if (!info->ubuf) {
- merror:
- if (ubuf) {
- info->isEncrypted = 0;
- memset(info->keys, 0, sizeof(info->keys));
- ckfree(ubuf);
- }
- ckfree(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));
- ckfree(ubuf);
+ ckfree(cbuf);
}
- goto wrapchan;
- }
- cerror:
- if (ubuf) {
- info->isEncrypted = 0;
- memset(info->keys, 0, sizeof(info->keys));
- ckfree(ubuf);
- }
- if (info->ubuf) {
- ckfree(info->ubuf);
- }
- ckfree(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;
- unsigned int 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 *) attemptckalloc(len);
- if (ubuf == NULL) {
- ckfree((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) {
+ ckfree(info->ubuf);
+ }
+ ZIPFS_MEM_ERROR(interp);
+ return TCL_ERROR;
+
+ corruptionError:
+ if (cbuf) {
+ memset(info->keys, 0, sizeof(info->keys));
+ ckfree(cbuf);
+ }
+ if (info->ubuf) {
+ ckfree(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 *) attemptckalloc(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 *)
+ attemptckalloc(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));
+ ckfree(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 *) attemptckalloc(len);
+ if (ubuf == NULL) {
+ goto memoryError;
+ }
+ for (j = 0; j < len; j++) {
+ ch = info->ubuf[j];
+ ubuf[j] = zdecode(info->keys, crc32tab, ch);
}
+ info->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));
+ ckfree(ubuf);
+ }
+ if (info->ubuf) {
+ ckfree(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));
+ ckfree(ubuf);
+ }
+ ZIPFS_MEM_ERROR(interp);
+ return TCL_ERROR;
}
/*
@@ -4319,9 +4666,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.
*
*-------------------------------------------------------------------------
*/
@@ -4331,16 +4683,31 @@ ZipFSOpenFileChannelProc(
Tcl_Interp *interp, /* Current interpreter. */
Tcl_Obj *pathPtr,
int mode,
- int permissions)
+ TCL_UNUSED(int) /* permissions */)
{
- int len;
+ 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, Tcl_GetStringFromObj(pathPtr, &len), 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, Tcl_GetString(pathPtr), wr, trunc);
}
/*
@@ -4365,13 +4732,11 @@ ZipFSStatProc(
Tcl_Obj *pathPtr,
Tcl_StatBuf *buf)
{
- int len;
-
pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
if (!pathPtr) {
return -1;
}
- return ZipEntryStat(Tcl_GetStringFromObj(pathPtr, &len), buf);
+ return ZipEntryStat(Tcl_GetString(pathPtr), buf);
}
/*
@@ -4396,13 +4761,11 @@ ZipFSAccessProc(
Tcl_Obj *pathPtr,
int mode)
{
- int len;
-
pathPtr = Tcl_FSGetNormalizedPath(NULL, pathPtr);
if (!pathPtr) {
return -1;
}
- return ZipEntryAccess(Tcl_GetStringFromObj(pathPtr, &len), mode);
+ return ZipEntryAccess(Tcl_GetString(pathPtr), mode);
}
/*
@@ -4434,6 +4797,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
@@ -4453,24 +4848,24 @@ 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, prefixLen, strip = 0;
- size_t len;
+ int scnt, l, dirOnly = -1, prefixLen, strip = 0, mounts = 0, 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);
}
/*
@@ -4483,105 +4878,56 @@ ZipFSMatchInDirectoryProc(
* The (normalized) path we're searching.
*/
- path = Tcl_GetString(normPathPtr);
- len = normPathPtr->length;
+ path = Tcl_GetStringFromObj(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);
+ ZipEntry *z = ZipFSLookup(path);
- 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));
- }
- }
+ 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 *) ckalloc(len + l + 2);
memcpy(pat, path, len);
@@ -4594,6 +4940,7 @@ 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);
@@ -4603,16 +4950,7 @@ ZipFSMatchInDirectoryProc(
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);
}
}
ckfree(pat);
@@ -4626,6 +4964,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 = Tcl_GetStringFromObj(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
@@ -4647,22 +5073,18 @@ 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_GetString(pathPtr);
+ path = Tcl_GetStringFromObj(pathPtr, &len);
if (strncmp(path, ZIPFS_VOLUME, ZIPFS_VOLUME_LEN) != 0) {
return -1;
}
- len = pathPtr->length;
-
ReadLock();
hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, path);
if (hPtr) {
@@ -4680,12 +5102,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;
@@ -4737,11 +5160,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",
@@ -4749,6 +5186,7 @@ ZipFSFileAttrStringsProc(
"-mount",
"-archive",
"-permissions",
+ "-crc",
NULL,
};
@@ -4800,27 +5238,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;
}
@@ -4853,10 +5295,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;
}
@@ -4958,7 +5398,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);
}
@@ -5074,8 +5514,10 @@ 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);
+ }
ensemble = TclMakeEnsemble(interp, "zipfs",
Tcl_IsSafe(interp) ? (initMap + 4) : initMap);
@@ -5093,7 +5535,7 @@ TclZipfs_Init(
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 */
}
@@ -5303,9 +5745,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;
}
@@ -5318,9 +5758,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;
}
@@ -5330,9 +5768,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 9fc84da..440bb9a 100644
--- a/generic/tclZlib.c
+++ b/generic/tclZlib.c
@@ -3957,7 +3957,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_PkgProvide(interp, "zlib", TCL_ZLIB_VERSION);
#endif
return Tcl_PkgProvide(interp, "tcl::zlib", TCL_ZLIB_VERSION);