Check-in [8b4b8de192]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:improve async mode and dir lookups in zipfs
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8b4b8de19229258280326307e5837654de84eee4
User & Date: chw 2019-09-19 04:51:12
Context
2019-09-19
04:51
add tcl upstream changes check-in: bde46f3e3e user: chw tags: trunk
04:51
improve async mode and dir lookups in zipfs check-in: 8b4b8de192 user: chw tags: trunk
2019-09-18
03:47
update tclJBlend to version 2.1 check-in: e575a41f73 user: chw tags: trunk
Changes

Changes to jni/tcl/generic/zipfs.c.

269
270
271
272
273
274
275



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291


292
293
294
295
296
297
298
...
363
364
365
366
367
368
369

370
371
372
373
374
375
376
....
1722
1723
1724
1725
1726
1727
1728


1729
1730
1731
1732
1733
1734
1735
....
1834
1835
1836
1837
1838
1839
1840




1841
1842
1843
1844
1845
1846
1847
....
1875
1876
1877
1878
1879
1880
1881


1882
1883
1884
1885
1886
1887
1888
....
1961
1962
1963
1964
1965
1966
1967




1968
1969
1970
1971
1972
1973
1974
....
3276
3277
3278
3279
3280
3281
3282

3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301



3302
3303
3304
3305
3306
3307
3308
....
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
....
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
....
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
....
4397
4398
4399
4400
4401
4402
4403



4404

4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416

4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
....
5071
5072
5073
5074
5075
5076
5077

5078
5079
5080
5081
5082
5083
5084
5085
5086
} ZipEvent;

/*
 * Global variables.
 *
 * Most are kept in single ZipFS struct. When build with threading
 * support this struct is protected by the ZipFSMutex (see below).



 *
 * The "fileHash" component is the process wide global table of all known
 * ZIP archive members in all mounted ZIP archives.
 *
 * The "zipHash" component is the process wide global table of all mounted
 * ZIP archive files.
 */

static struct {
    int initialized;		/* True when initialized */
    int lock;			/* RW lock, see below */
    int waiters;		/* RW lock, see below */
    int wrmax;			/* Maximum write size of a file */
    int idCount;		/* Counter for channel names */
    Tcl_HashTable fileHash;	/* File name to ZipEntry mapping */
    Tcl_HashTable zipHash;	/* Mount to ZipFile mapping */


} ZipFS = {
    0, 0, 0, 0, 0,
};

/*
 * For password rotation.
 */
................................................................................

/*
 * Forward declarations.
 */

static ZipEntry *		ZipFSLookup(char *filename);
static ThreadSpecificData *	ZipChannelInit(void);


/*
 * For embedding a ZIP in the binary.
 */

#if defined(ZIPFS_IN_TCL) || defined(ZIPFS_IN_TK)
#if defined(MAC_OSX_TCL)
................................................................................
	    /* skip it */
	    Tcl_Free((char *) z);
	} else {
	    Tcl_SetHashValue(hPtr, (ClientData) z);
	    z->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
	    z->next = zf->entries;
	    zf->entries = z;


	}
    }
    q = zf->data + zf->centoffs;
    Tcl_DStringInit(&fpBuf);
    for (i = 0; i < zf->nfiles; i++) {
	int pathlen, comlen, extra, isdir = 0, dosTime, dosDate, nbcompr, offs;
	unsigned char *lq, *gq = NULL;
................................................................................
	if (!isNew) {
	    /* should not happen but skip it anyway */
	    Tcl_Free((char *) z);
	} else {
	    Tcl_SetHashValue(hPtr, (ClientData) z);
	    z->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
	    z->next = zf->entries;




	    zf->entries = z;
	    if (isdir && (mntpt[0] == '\0') && (z->depth == 1)) {
		z->tnext = zf->topents;
		zf->topents = z;
	    }
	    if (!z->isdir && (z->depth > 1)) {
		char *dir, *end;
................................................................................
		    if (!isNew) {
			/* should not happen but skip it anyway */
			Tcl_Free((char *) zd);
		    } else {
			Tcl_SetHashValue(hPtr, (ClientData) zd);
			zd->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
			zd->next = zf->entries;


			zf->entries = zd;
			if ((mntpt[0] == '\0') && (zd->depth == 1)) {
			    zd->tnext = zf->topents;
			    zf->topents = zd;
			}
		    }
		    end = strrchr(dir, '/');
................................................................................
    }
    Tcl_DeleteHashEntry(hPtr);
    for (z = zf->entries; z; z = znext) {
	znext = z->next;
	hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, z->name);
	if (hPtr) {
	    Tcl_DeleteHashEntry(hPtr);




	}
	if (z->data != NULL) {
	    Tcl_Free((char *) z->data);
	}
	Tcl_Free((char *) z);
    }
    ZipFSCloseArchive(interp, zf);
................................................................................
 *
 *-------------------------------------------------------------------------
 */

static void
ZipChannelWatchChannel(ClientData instanceData, int mask)
{

    ZipChannel *info = (ZipChannel *) instanceData;
    Tcl_HashEntry *hPtr;
    int isNew;
    ThreadSpecificData *tsdPtr =
	(ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
    static const Tcl_Time blockTime = { 0, 0 };

    mask &= ~TCL_EXCEPTION; /* not supported at all */
    if ((mask & TCL_WRITABLE) && !info->iswr) {
	mask &= ~TCL_WRITABLE;
    }
    info->evmask &= ~(TCL_READABLE | TCL_WRITABLE);
    info->evmask |= mask & (TCL_READABLE | TCL_WRITABLE);
    if (info->evmask & (TCL_READABLE | TCL_WRITABLE)) {
	if ((tsdPtr != NULL) && tsdPtr->initialized) {
	    hPtr = Tcl_CreateHashEntry(&tsdPtr->chanTab, info, &isNew);
	    if (isNew) {
		Tcl_SetHashValue(hPtr, info);
		Tcl_SetMaxBlockTime(&blockTime);



	    }
	}
    } else {
	info->evmask &= ~(ZIPCHANNEL_NONBLOCK);
	if ((tsdPtr != NULL) && tsdPtr->initialized) {
	    hPtr = Tcl_FindHashEntry(&tsdPtr->chanTab, info);
	    if (hPtr != NULL) {
................................................................................
	mask = info->evmask & (TCL_READABLE | TCL_WRITABLE);
	if (mask) {
	    Tcl_NotifyChannel(info->chan, mask);
	}
    }
    return 1;
}
 
/*
 *----------------------------------------------------------------------
 *
 * ZipChannelSetupProc --
 *
 *	This function is invoked before Tcl_DoOneEvent blocks waiting
 *	for an event.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Adjusts the block time if needed.
 *
 *----------------------------------------------------------------------
 */

static void
ZipChannelSetupProc(ClientData clientData, int flags)
{
    Tcl_HashEntry *hPtr;
    Tcl_HashSearch search;
    ThreadSpecificData *tsdPtr =
	(ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);
    static const Tcl_Time blockTime = { 0, 0 };

    if ((tsdPtr == NULL) || !tsdPtr->initialized) {
	return;
    }
    if (!(flags & TCL_FILE_EVENTS)) {
	return;
    }
    hPtr = Tcl_FirstHashEntry(&tsdPtr->chanTab, &search);
    if (hPtr != NULL) {
	Tcl_SetMaxBlockTime(&blockTime);
    }
}
 
/*
 *----------------------------------------------------------------------
 *
 * ZipChannelCheckProc --
 *
 *	This function is called by Tcl_DoOneEvent to check the
 *	event source for events.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May queue an event.
 *
 *----------------------------------------------------------------------
 */

static void
ZipChannelCheckProc(ClientData clientData, int flags)
{
    ZipEvent *event;
    ZipChannel *info;
    Tcl_HashEntry *hPtr;
    Tcl_HashSearch search;
    ThreadSpecificData *tsdPtr =
	(ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);

    if ((tsdPtr == NULL) || !tsdPtr->initialized) {
	return;
    }
    if (!(flags & TCL_FILE_EVENTS)) {
	return;
    }
    hPtr = Tcl_FirstHashEntry(&tsdPtr->chanTab, &search);
    while (hPtr != NULL) {
	info = (ZipChannel *) Tcl_GetHashValue(hPtr);
	if (info->evmask && !(info->evmask & ZIPCHANNEL_PENDING)) {
	    info->evmask |= ZIPCHANNEL_PENDING;
	    event = ckalloc(sizeof(ZipEvent));
	    event->header.proc = ZipEventProc;
	    event->info = info;
	    Tcl_QueueEvent((Tcl_Event *) event, TCL_QUEUE_TAIL);
	}
	hPtr = Tcl_NextHashEntry(&search);
    }
}
 
/*
 *-------------------------------------------------------------------------
 *
 * ZipChannelExitHandler --
 *
 *	This function is called to cleanup thread specific channel
 *	information.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Destroys the ZIP channel event source.
 *
 *-------------------------------------------------------------------------
 */

static void
ZipChannelExitHandler(ClientData clientData)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    if (tsdPtr->initialized) {
	Tcl_DeleteHashTable(&tsdPtr->chanTab);
	Tcl_DeleteEventSource(ZipChannelSetupProc, ZipChannelCheckProc, NULL);
	tsdPtr->initialized = 0;
    }
}
 
/*
 *-------------------------------------------------------------------------
 *
................................................................................
 *	This function is called to setup thread specific channel
 *	information.
 *
 * Results:
 *	Pointer to thread specific data.
 *
 * Side effects:
 *	Adds an event source and a per thread exit handler.
 *
 *-------------------------------------------------------------------------
 */

static ThreadSpecificData *
ZipChannelInit(void)
{
    ThreadSpecificData *tsdPtr =
	(ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);

    if (tsdPtr == NULL) {
	tsdPtr = TCL_TSD_INIT(&dataKey);
	Tcl_InitHashTable(&tsdPtr->chanTab, TCL_ONE_WORD_KEYS);
	Tcl_CreateEventSource(ZipChannelSetupProc, ZipChannelCheckProc, NULL);
	Tcl_CreateThreadExitHandler(ZipChannelExitHandler, NULL);
	tsdPtr->initialized = 1;
    }
    return tsdPtr;
}
 
/*
................................................................................
		       Tcl_StringCaseMatch(zf->mntpt + len + 1, pattern, 0)) {
		if (!matchHidden) {
		    p = strrchr(zf->mntpt, '/');
		    if ((p != NULL) && (p[1] == '.')) {
			goto end;
		    }
		}
		if (prefix != NULL) {
		    Tcl_DStringAppend(&dsPref, zf->mntpt, zf->mntptlen);
		    Tcl_ListObjAppendElement(NULL, result,
			Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
			    Tcl_DStringLength(&dsPref)));
		    Tcl_DStringSetLength(&dsPref, prefixLen);
		} else {
		    Tcl_ListObjAppendElement(NULL, result,
			Tcl_NewStringObj(zf->mntpt, zf->mntptlen));
		}
	    }
	}
	goto end;
    }
    if ((pattern == NULL) || (pattern[0] == '\0')) {
	hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, path);
	if (hPtr != NULL) {
................................................................................
    }
    if ((len > 1) || (pat[0] != '/')) {
	pat[len] = '/';
	++len;
    }
    memcpy(pat + len, pattern, l + 1);
    scnt = CountSlashes(pat);



    for (hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);

	 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
	ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);

	if ((dirOnly >= 0) &&
	    ((dirOnly && !z->isdir) || (!dirOnly && z->isdir))) {
	    continue;
	}
#if HAS_DRIVES
	if (drive && (drive != z->zipfile->mntdrv)) {
	    continue;
	}
#endif

	if ((z->depth == scnt) && Tcl_StringCaseMatch(z->name, pat, 0)) {
	    if (!matchHidden) {
		p = strrchr(z->name, '/');
		if ((p != NULL) && (p[1] == '.')) {
		    continue;
		}
	    }
	    if (prefix != NULL) {
		Tcl_DStringAppend(&dsPref, z->name + strip, -1);
		Tcl_ListObjAppendElement(NULL, result,
		    Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
			Tcl_DStringLength(&dsPref)));
		Tcl_DStringSetLength(&dsPref, prefixLen);
................................................................................
	 * Inflate condition variable.
	 */
	Tcl_MutexLock(&ZipFSMutex);
	Tcl_ConditionWait(&ZipFSCond, &ZipFSMutex, &t);
	Tcl_MutexUnlock(&ZipFSMutex);
#endif
	Tcl_FSRegister(NULL, &zipfsFilesystem);

	Tcl_InitHashTable(&ZipFS.fileHash, TCL_STRING_KEYS);
	Tcl_InitHashTable(&ZipFS.zipHash, TCL_STRING_KEYS);
	ZipFS.initialized = ZipFS.idCount = 1;
#if defined(ZIPFS_IN_TCL) || defined(ZIPFS_IN_TK)
	if (interp != NULL) {
	    Tcl_StaticPackage(interp, "zipfs", Zipfs_Init, Zipfs_SafeInit);
	}
#endif
    }







>
>
>



<
<
<








<

>
>







 







>







 







>
>







 







>
>
>
>







 







>
>







 







>
>
>
>







 







>





<












|
>
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<













|











<







 







|













<







 







<
<
|
<
<
<
<
<
|
<







 







>
>
>
|
>
|











>
|
|
<
<
|
<







 







>

|







269
270
271
272
273
274
275
276
277
278
279
280
281



282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297
298
299
...
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
....
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
....
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
....
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
....
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
....
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302

3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
....
3880
3881
3882
3883
3884
3885
3886





















































































3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911

3912
3913
3914
3915
3916
3917
3918
....
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941

3942
3943
3944
3945
3946
3947
3948
....
4274
4275
4276
4277
4278
4279
4280


4281





4282

4283
4284
4285
4286
4287
4288
4289
....
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345


4346

4347
4348
4349
4350
4351
4352
4353
....
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
} ZipEvent;

/*
 * Global variables.
 *
 * Most are kept in single ZipFS struct. When build with threading
 * support this struct is protected by the ZipFSMutex (see below).
 *
 * The "zipHash" component is the process wide global table of all mounted
 * ZIP archive files.
 *
 * The "fileHash" component is the process wide global table of all known
 * ZIP archive members in all mounted ZIP archives.



 */

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 idCount;		/* Counter for channel names */

    Tcl_HashTable zipHash;	/* Mount to ZipFile mapping */
    Tcl_HashTable fileHash;	/* File name to ZipEntry mapping */
    Tcl_HashTable dirHash;	/* Like fileHash but directories only */
} ZipFS = {
    0, 0, 0, 0, 0,
};

/*
 * For password rotation.
 */
................................................................................

/*
 * Forward declarations.
 */

static ZipEntry *		ZipFSLookup(char *filename);
static ThreadSpecificData *	ZipChannelInit(void);
static int			ZipEventProc(Tcl_Event *evPtr, int flags);

/*
 * For embedding a ZIP in the binary.
 */

#if defined(ZIPFS_IN_TCL) || defined(ZIPFS_IN_TK)
#if defined(MAC_OSX_TCL)
................................................................................
	    /* skip it */
	    Tcl_Free((char *) z);
	} else {
	    Tcl_SetHashValue(hPtr, (ClientData) z);
	    z->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
	    z->next = zf->entries;
	    zf->entries = z;
	    hPtr = Tcl_CreateHashEntry(&ZipFS.dirHash, z, &isNew);
	    Tcl_SetHashValue(hPtr, (ClientData) z);
	}
    }
    q = zf->data + zf->centoffs;
    Tcl_DStringInit(&fpBuf);
    for (i = 0; i < zf->nfiles; i++) {
	int pathlen, comlen, extra, isdir = 0, dosTime, dosDate, nbcompr, offs;
	unsigned char *lq, *gq = NULL;
................................................................................
	if (!isNew) {
	    /* should not happen but skip it anyway */
	    Tcl_Free((char *) z);
	} else {
	    Tcl_SetHashValue(hPtr, (ClientData) z);
	    z->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
	    z->next = zf->entries;
	    if (z->isdir) {
		hPtr = Tcl_CreateHashEntry(&ZipFS.dirHash, z, &isNew);
		Tcl_SetHashValue(hPtr, (ClientData) z);
	    }
	    zf->entries = z;
	    if (isdir && (mntpt[0] == '\0') && (z->depth == 1)) {
		z->tnext = zf->topents;
		zf->topents = z;
	    }
	    if (!z->isdir && (z->depth > 1)) {
		char *dir, *end;
................................................................................
		    if (!isNew) {
			/* should not happen but skip it anyway */
			Tcl_Free((char *) zd);
		    } else {
			Tcl_SetHashValue(hPtr, (ClientData) zd);
			zd->name = Tcl_GetHashKey(&ZipFS.fileHash, hPtr);
			zd->next = zf->entries;
			hPtr = Tcl_CreateHashEntry(&ZipFS.dirHash, zd, &isNew);
			Tcl_SetHashValue(hPtr, (ClientData) zd);
			zf->entries = zd;
			if ((mntpt[0] == '\0') && (zd->depth == 1)) {
			    zd->tnext = zf->topents;
			    zf->topents = zd;
			}
		    }
		    end = strrchr(dir, '/');
................................................................................
    }
    Tcl_DeleteHashEntry(hPtr);
    for (z = zf->entries; z; z = znext) {
	znext = z->next;
	hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, z->name);
	if (hPtr) {
	    Tcl_DeleteHashEntry(hPtr);
	}
	hPtr = Tcl_FindHashEntry(&ZipFS.dirHash, z);
	if (hPtr) {
	    Tcl_DeleteHashEntry(hPtr);
	}
	if (z->data != NULL) {
	    Tcl_Free((char *) z->data);
	}
	Tcl_Free((char *) z);
    }
    ZipFSCloseArchive(interp, zf);
................................................................................
 *
 *-------------------------------------------------------------------------
 */

static void
ZipChannelWatchChannel(ClientData instanceData, int mask)
{
    ZipEvent *event;
    ZipChannel *info = (ZipChannel *) instanceData;
    Tcl_HashEntry *hPtr;
    int isNew;
    ThreadSpecificData *tsdPtr =
	(ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);


    mask &= ~TCL_EXCEPTION; /* not supported at all */
    if ((mask & TCL_WRITABLE) && !info->iswr) {
	mask &= ~TCL_WRITABLE;
    }
    info->evmask &= ~(TCL_READABLE | TCL_WRITABLE);
    info->evmask |= mask & (TCL_READABLE | TCL_WRITABLE);
    if (info->evmask & (TCL_READABLE | TCL_WRITABLE)) {
	if ((tsdPtr != NULL) && tsdPtr->initialized) {
	    hPtr = Tcl_CreateHashEntry(&tsdPtr->chanTab, info, &isNew);
	    if (isNew) {
		Tcl_SetHashValue(hPtr, info);
		event = ckalloc(sizeof(ZipEvent));
		event->header.proc = ZipEventProc;
		event->info = info;
		Tcl_QueueEvent((Tcl_Event *) event, TCL_QUEUE_TAIL);
	    }
	}
    } else {
	info->evmask &= ~(ZIPCHANNEL_NONBLOCK);
	if ((tsdPtr != NULL) && tsdPtr->initialized) {
	    hPtr = Tcl_FindHashEntry(&tsdPtr->chanTab, info);
	    if (hPtr != NULL) {
................................................................................
	mask = info->evmask & (TCL_READABLE | TCL_WRITABLE);
	if (mask) {
	    Tcl_NotifyChannel(info->chan, mask);
	}
    }
    return 1;
}





















































































 
/*
 *-------------------------------------------------------------------------
 *
 * ZipChannelExitHandler --
 *
 *	This function is called to cleanup thread specific channel
 *	information.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *-------------------------------------------------------------------------
 */

static void
ZipChannelExitHandler(ClientData clientData)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    if (tsdPtr->initialized) {
	Tcl_DeleteHashTable(&tsdPtr->chanTab);

	tsdPtr->initialized = 0;
    }
}
 
/*
 *-------------------------------------------------------------------------
 *
................................................................................
 *	This function is called to setup thread specific channel
 *	information.
 *
 * Results:
 *	Pointer to thread specific data.
 *
 * Side effects:
 *	None.
 *
 *-------------------------------------------------------------------------
 */

static ThreadSpecificData *
ZipChannelInit(void)
{
    ThreadSpecificData *tsdPtr =
	(ThreadSpecificData *) TclThreadDataKeyGet(&dataKey);

    if (tsdPtr == NULL) {
	tsdPtr = TCL_TSD_INIT(&dataKey);
	Tcl_InitHashTable(&tsdPtr->chanTab, TCL_ONE_WORD_KEYS);

	Tcl_CreateThreadExitHandler(ZipChannelExitHandler, NULL);
	tsdPtr->initialized = 1;
    }
    return tsdPtr;
}
 
/*
................................................................................
		       Tcl_StringCaseMatch(zf->mntpt + len + 1, pattern, 0)) {
		if (!matchHidden) {
		    p = strrchr(zf->mntpt, '/');
		    if ((p != NULL) && (p[1] == '.')) {
			goto end;
		    }
		}


		Tcl_ListObjAppendElement(NULL, result,





		    Tcl_NewStringObj(zf->mntpt, zf->mntptlen));

	    }
	}
	goto end;
    }
    if ((pattern == NULL) || (pattern[0] == '\0')) {
	hPtr = Tcl_FindHashEntry(&ZipFS.fileHash, path);
	if (hPtr != NULL) {
................................................................................
    }
    if ((len > 1) || (pat[0] != '/')) {
	pat[len] = '/';
	++len;
    }
    memcpy(pat + len, pattern, l + 1);
    scnt = CountSlashes(pat);
    if (dirOnly > 0) {
	hPtr = Tcl_FirstHashEntry(&ZipFS.dirHash, &search);
    } else {
	hPtr = Tcl_FirstHashEntry(&ZipFS.fileHash, &search);
    }
    for (; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
	ZipEntry *z = (ZipEntry *) Tcl_GetHashValue(hPtr);

	if ((dirOnly >= 0) &&
	    ((dirOnly && !z->isdir) || (!dirOnly && z->isdir))) {
	    continue;
	}
#if HAS_DRIVES
	if (drive && (drive != z->zipfile->mntdrv)) {
	    continue;
	}
#endif
	if ((z->depth == scnt) && (strncmp(z->name, pat, len) == 0) &&
	    Tcl_StringCaseMatch(z->name + len, pat + len, 0)) {
	    if (!matchHidden && (z->name[len] == '.')) {


		continue;

	    }
	    if (prefix != NULL) {
		Tcl_DStringAppend(&dsPref, z->name + strip, -1);
		Tcl_ListObjAppendElement(NULL, result,
		    Tcl_NewStringObj(Tcl_DStringValue(&dsPref),
			Tcl_DStringLength(&dsPref)));
		Tcl_DStringSetLength(&dsPref, prefixLen);
................................................................................
	 * Inflate condition variable.
	 */
	Tcl_MutexLock(&ZipFSMutex);
	Tcl_ConditionWait(&ZipFSCond, &ZipFSMutex, &t);
	Tcl_MutexUnlock(&ZipFSMutex);
#endif
	Tcl_FSRegister(NULL, &zipfsFilesystem);
	Tcl_InitHashTable(&ZipFS.zipHash, TCL_STRING_KEYS);
	Tcl_InitHashTable(&ZipFS.fileHash, TCL_STRING_KEYS);
	Tcl_InitHashTable(&ZipFS.dirHash, TCL_ONE_WORD_KEYS);
	ZipFS.initialized = ZipFS.idCount = 1;
#if defined(ZIPFS_IN_TCL) || defined(ZIPFS_IN_TK)
	if (interp != NULL) {
	    Tcl_StaticPackage(interp, "zipfs", Zipfs_Init, Zipfs_SafeInit);
	}
#endif
    }