1/*
2 * This file implements a family of commands for sharing variables
3 * between threads.
4 *
5 * Initial code is taken from nsd/tclvar.c found in AOLserver 3.+
6 * distribution and modified to support Tcl 8.0+ command object interface
7 * and internal storage in private shared Tcl objects.
8 *
9 * Copyright (c) 2002 by Zoran Vasiljevic.
10 *
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id: threadSvCmd.c,v 1.50 2010/03/31 08:50:24 vasiljevic Exp $
15 * ----------------------------------------------------------------------------
16 */
17
18#include "threadSvCmd.h"
19
20#include "threadSvListCmd.h"    /* Shared variants of list commands */
21#include "threadSvKeylistCmd.h" /* Shared variants of list commands */
22#include "psGdbm.h"             /* The gdbm persistent store implementation */
23
24#ifdef NS_AOLSERVER
25# define HIDE_DOTNAMES       /* tsv::names cmd does not list .<name> arrays */
26#endif
27
28/*
29 * Number of buckets to spread shared arrays into. Each bucket is
30 * associated with one mutex so locking a bucket locks all arrays
31 * in that bucket as well. The number of buckets should be a prime.
32 */
33
34#define NUMBUCKETS 31
35
36/*
37 * Number of object containers
38 * to allocate in one shot.
39 */
40
41#define OBJS_TO_ALLOC_EACH_TIME 100
42
43/*
44 * Handle hiding of errorLine in 8.6
45 */
46#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION < 6)
47#define ERRORLINE(interp) ((interp)->errorLine)
48#else
49#define ERRORLINE(interp) (Tcl_GetErrorLine(interp))
50#endif
51
52/*
53 * Reference to Tcl object types used in object-copy code.
54 * Those are referenced read-only, thus no mutex protection.
55 */
56
57static const Tcl_ObjType* booleanObjTypePtr;
58static const Tcl_ObjType* byteArrayObjTypePtr;
59static const Tcl_ObjType* doubleObjTypePtr;
60static const Tcl_ObjType* intObjTypePtr;
61static const Tcl_ObjType* stringObjTypePtr;
62
63/*
64 * In order to be fully stub enabled, a small
65 * hack is needed to query the tclEmptyStringRep
66 * global symbol defined by Tcl. See Sv_Init.
67 */
68
69char *Sv_tclEmptyStringRep = NULL;
70
71/*
72 * Global variables used within this file.
73 */
74
75static Bucket*    buckets;      /* Array of buckets. */
76static Tcl_Mutex  bucketsMutex; /* Protects the array of buckets */
77
78static SvCmdInfo* svCmdInfo;    /* Linked list of registered commands */
79static RegType*   regType;      /* Linked list of registered obj types */
80static PsStore*   psStore;      /* Linked list of registered pers. stores */
81
82static Tcl_Mutex  svMutex;      /* Protects inserts into above lists */
83static Tcl_Mutex  initMutex;    /* Serializes initialization issues */
84
85/*
86 * The standard commands found in AOLserver nsv_* interface.
87 * For sharp-eye readers: the implementaion of the "lappend" command
88 * is moved to new list-command package, since it realy belongs there.
89 */
90
91static Tcl_ObjCmdProc SvObjObjCmd;
92static Tcl_ObjCmdProc SvAppendObjCmd;
93static Tcl_ObjCmdProc SvIncrObjCmd;
94static Tcl_ObjCmdProc SvSetObjCmd;
95static Tcl_ObjCmdProc SvExistsObjCmd;
96static Tcl_ObjCmdProc SvGetObjCmd;
97static Tcl_ObjCmdProc SvArrayObjCmd;
98static Tcl_ObjCmdProc SvUnsetObjCmd;
99static Tcl_ObjCmdProc SvNamesObjCmd;
100
101/*
102 * New commands added to
103 * standard set of nsv_*
104 */
105
106static Tcl_ObjCmdProc SvPopObjCmd;
107static Tcl_ObjCmdProc SvMoveObjCmd;
108static Tcl_ObjCmdProc SvLockObjCmd;
109
110/*
111 * Forward declarations for functions to
112 * manage buckets, arrays and shared objects.
113 */
114
115static Container* CreateContainer(Array*, Tcl_HashEntry*, Tcl_Obj*);
116static Container* AcquireContainer(Array*, const char*, int);
117
118static Array* CreateArray(Bucket*, const char*);
119static Array* LockArray(Tcl_Interp*, const char*, int);
120
121static int ReleaseContainer(Tcl_Interp*, Container*, int);
122static int DeleteContainer(Container*);
123static int FlushArray(Array*);
124static int DeleteArray(Array*);
125
126static void SvAllocateContainers(Bucket*);
127static void SvRegisterStdCommands(void);
128
129#ifdef SV_FINALIZE
130static void SvFinalizeContainers(Bucket*);
131static void SvFinalize(ClientData);
132#endif /* SV_FINALIZE */
133
134static PsStore* GetPsStore(char *handle);
135
136static int SvObjDispatchObjCmd _ANSI_ARGS_ ((ClientData arg,
137            Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]));
138
139/*
140 *-----------------------------------------------------------------------------
141 *
142 * Sv_RegisterCommand --
143 *
144 *      Utility to register commands to be loaded at module start.
145 *
146 * Results:
147 *      None.
148 *
149 * Side effects;
150 *      New command will be added to a linked list of registered commands.
151 *
152 *-----------------------------------------------------------------------------
153 */
154
155void
156Sv_RegisterCommand(cmdName, objProc, delProc, clientData)
157    const char *cmdName;                /* Name of command to register */
158    Tcl_ObjCmdProc *objProc;            /* Object-based command procedure */
159    Tcl_CmdDeleteProc *delProc;         /* Command delete procedure */
160    ClientData clientData;              /* Private data ptr to pass to cmd */
161{
162    int len = strlen(cmdName) + strlen(TSV_CMD_PREFIX);
163    SvCmdInfo *newCmd = (SvCmdInfo*)Tcl_Alloc(sizeof(SvCmdInfo) + len + 1);
164
165    /*
166     * Setup new command structure
167     */
168
169    newCmd->cmdName = (char*)((char*)newCmd + sizeof(SvCmdInfo));
170
171    newCmd->objProcPtr = objProc;
172    newCmd->delProcPtr = delProc;
173    newCmd->clientData = clientData;
174
175    /*
176     * Rewrite command name. This is needed so we can
177     * easily turn-on the compatiblity with AOLserver
178     * command names.
179     */
180
181    strcpy(newCmd->cmdName, TSV_CMD_PREFIX);
182    strcat(newCmd->cmdName, cmdName);
183    newCmd->name = newCmd->cmdName + strlen(TSV_CMD_PREFIX);
184
185    /*
186     * Plug-in in shared list of commands.
187     */
188
189    Tcl_MutexLock(&svMutex);
190    if (svCmdInfo == NULL) {
191        svCmdInfo = newCmd;
192        newCmd->nextPtr = NULL;
193    } else {
194        newCmd->nextPtr = svCmdInfo;
195        svCmdInfo = newCmd;
196    }
197    Tcl_MutexUnlock(&svMutex);
198
199    return;
200}
201
202/*
203 *-----------------------------------------------------------------------------
204 *
205 * Sv_RegisterObjType --
206 *
207 *      Registers custom object duplicator function for a specific
208 *      object type. Registered function will be called by the
209 *      private object creation routine every time an object is
210 *      plugged out or in the shared array. This way we assure that
211 *      Tcl objects do not get shared per-reference between threads.
212 *
213 * Results:
214 *      None.
215 *
216 * Side effects;
217 *      Memory gets allocated.
218 *
219 *-----------------------------------------------------------------------------
220 */
221
222void
223Sv_RegisterObjType(typePtr, dupProc)
224    const Tcl_ObjType *typePtr;               /* Type of object to register */
225    Tcl_DupInternalRepProc *dupProc;    /* Custom object duplicator */
226{
227    RegType *newType = (RegType*)Tcl_Alloc(sizeof(RegType));
228
229    /*
230     * Setup new type structure
231     */
232
233    newType->typePtr = typePtr;
234    newType->dupIntRepProc = dupProc;
235
236    /*
237     * Plug-in in shared list
238     */
239
240    Tcl_MutexLock(&svMutex);
241    newType->nextPtr = regType;
242    regType = newType;
243    Tcl_MutexUnlock(&svMutex);
244}
245
246/*
247 *-----------------------------------------------------------------------------
248 *
249 * Sv_RegisterPsStore --
250 *
251 *      Registers a handler to the persistent storage.
252 *
253 * Results:
254 *      None.
255 *
256 * Side effects;
257 *      Memory gets allocated.
258 *
259 *-----------------------------------------------------------------------------
260 */
261
262void
263Sv_RegisterPsStore(psStorePtr)
264     PsStore *psStorePtr;
265{
266
267    PsStore *psPtr = (PsStore*)Tcl_Alloc(sizeof(PsStore));
268
269    *psPtr = *psStorePtr;
270
271    /*
272     * Plug-in in shared list
273     */
274
275    Tcl_MutexLock(&svMutex);
276    if (psStore == NULL) {
277        psStore = psPtr;
278        psStore->nextPtr = NULL;
279    } else {
280        psPtr->nextPtr = psStore;
281        psStore = psPtr;
282    }
283    Tcl_MutexUnlock(&svMutex);
284}
285
286/*
287 *-----------------------------------------------------------------------------
288 *
289 * Sv_GetContainer --
290 *
291 *      This is the workhorse of the module. It returns the container
292 *      with the shared Tcl object. It also locks the container, so
293 *      when finished with operation on the Tcl object, one has to
294 *      unlock the container by calling the Sv_PutContainer().
295 *      If instructed, this command might also create new container
296 *      with empty Tcl object.
297 *
298 * Results:
299 *      A standard Tcl result.
300 *
301 * Side effects:
302 *      New container might be created.
303 *
304 *-----------------------------------------------------------------------------
305 */
306
307int
308Sv_GetContainer(interp, objc, objv, retObj, offset, flags)
309    Tcl_Interp *interp;                 /* Current interpreter. */
310    int objc;                           /* Number of arguments */
311    Tcl_Obj *const objv[];              /* Argument objects. */
312    Container **retObj;                 /* OUT: shared object container */
313    int *offset;                        /* Shift in argument list */
314    int flags;                          /* Options for locking shared array */
315{
316    const char *array, *key;
317
318    if (*retObj == NULL) {
319        Array *arrayPtr = NULL;
320
321        /*
322         * Parse mandatory arguments: <cmd> array key
323         */
324
325        if (objc < 3) {
326            Tcl_WrongNumArgs(interp, 1, objv, "array key ?args?");
327            return TCL_ERROR;
328        }
329
330        array = Tcl_GetString(objv[1]);
331        key   = Tcl_GetString(objv[2]);
332
333        *offset = 3; /* Consumed three arguments: cmd, array, key */
334
335        /*
336         * Lock the shared array and locate the shared object
337         */
338
339        arrayPtr = LockArray(interp, array, flags);
340        if (arrayPtr == NULL) {
341            return TCL_BREAK;
342        }
343        *retObj = AcquireContainer(arrayPtr, Tcl_GetString(objv[2]), flags);
344        if (*retObj == NULL) {
345            UnlockArray(arrayPtr);
346            Tcl_AppendResult(interp, "no key ", array, "(", key, ")", NULL);
347            return TCL_BREAK;
348        }
349    } else {
350        Tcl_HashTable *handles = &((*retObj)->bucketPtr->handles);
351        LOCK_CONTAINER(*retObj);
352        if (Tcl_FindHashEntry(handles, (char*)(*retObj)) == NULL) {
353            UNLOCK_CONTAINER(*retObj);
354            Tcl_SetResult(interp, "key has been deleted", TCL_STATIC);
355            return TCL_BREAK;
356        }
357        *offset = 2; /* Consumed two arguments: object, cmd */
358    }
359
360    return TCL_OK;
361}
362
363/*
364 *-----------------------------------------------------------------------------
365 *
366 * Sv_PutContainer --
367 *
368 *      Releases the container obtained by the Sv_GetContainer.
369 *
370 * Results:
371 *      A standard Tcl result.
372 *
373 * Side effects:
374 *      For bound arrays, update the underlying persistent storage.
375 *
376 *-----------------------------------------------------------------------------
377 */
378
379int
380Sv_PutContainer(interp, svObj, mode)
381    Tcl_Interp *interp;               /* For error reporting; might be NULL */
382    Container *svObj;                 /* Shared object container */
383    int mode;                         /* One of SV_XXX modes */
384{
385    int ret;
386
387    ret = ReleaseContainer(interp, svObj, mode);
388    UnlockArray(svObj->arrayPtr);
389
390    return ret;
391}
392
393/*
394 *-----------------------------------------------------------------------------
395 *
396 * GetPsStore --
397 *
398 *      Performs a lookup in the list of registered persistent storage
399 *      handlers. If the match is found, duplicates the persistent
400 *      storage record and passes the copy to the caller.
401 *
402 * Results:
403 *      Pointer to the newly allocated persistent storage handler. Caller
404 *      must free this block when done with it. If none found, returns NULL,
405 *
406 * Side effects;
407 *      Memory gets allocated. Caller should free the return value of this
408 *      function using Tcl_Free().
409 *
410 *-----------------------------------------------------------------------------
411 */
412
413static PsStore*
414GetPsStore(char *handle)
415{
416    int i;
417    char *type = handle, *addr, *delimiter = strchr(handle, ':');
418    PsStore *tmpPtr, *psPtr = NULL;
419
420    /*
421     * Expect the handle in the following format: <type>:<address>
422     * where "type" must match one of the registered presistent store
423     * types (gdbm, tcl, whatever) and <address> is what is passed to
424     * the open procedure of the registered store.
425     *
426     * Example: gdbm:/path/to/gdbm/file
427     */
428
429    /*
430     * Try to see wether some array is already bound to the
431     * same persistent storage address.
432     */
433
434    for (i = 0; i < NUMBUCKETS; i++) {
435        Tcl_HashSearch search;
436        Tcl_HashEntry *hPtr;
437        Bucket *bucketPtr = &buckets[i];
438        LOCK_BUCKET(bucketPtr);
439        hPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search);
440        while (hPtr) {
441            Array *arrayPtr = (Array*)Tcl_GetHashValue(hPtr);
442            if (arrayPtr->bindAddr && arrayPtr->psPtr) {
443                if (strcmp(arrayPtr->bindAddr, handle) == 0) {
444                    UNLOCK_BUCKET(bucketPtr);
445                    return NULL; /* Array already bound */
446                }
447            }
448            hPtr = Tcl_NextHashEntry(&search);
449        }
450        UNLOCK_BUCKET(bucketPtr);
451    }
452
453    /*
454     * Split the address and storage handler
455     */
456
457    if (delimiter == NULL) {
458        addr = NULL;
459    } else {
460        *delimiter = 0;
461        addr = delimiter + 1;
462    }
463
464    /*
465     * No array was bound to the same persistent storage.
466     * Lookup the persistent storage to bind to.
467     */
468
469    Tcl_MutexLock(&svMutex);
470    for (tmpPtr = psStore; tmpPtr; tmpPtr = tmpPtr->nextPtr) {
471        if (strcmp(tmpPtr->type, type) == 0) {
472            tmpPtr->psHandle = (*tmpPtr->psOpen)(addr);
473            if (tmpPtr->psHandle) {
474                psPtr = (PsStore*)Tcl_Alloc(sizeof(PsStore));
475                *psPtr = *tmpPtr;
476                psPtr->nextPtr = NULL;
477            }
478            break;
479        }
480    }
481    Tcl_MutexUnlock(&svMutex);
482
483    if (delimiter) {
484        *delimiter = ':';
485    }
486
487    return psPtr;
488}
489
490/*
491 *-----------------------------------------------------------------------------
492 *
493 * AcquireContainer --
494 *
495 *      Finds a variable within an array and returns it's container.
496 *
497 * Results:
498 *      Pointer to variable object.
499 *
500 * Side effects;
501 *      New variable may be created. For bound arrays, try to locate
502 *      the key in the persistent storage as well.
503 *
504 *-----------------------------------------------------------------------------
505 */
506
507static Container *
508AcquireContainer(arrayPtr, key, flags)
509    Array *arrayPtr;
510    const char *key;
511    int flags;
512{
513    int new;
514    Tcl_Obj *tclObj = NULL;
515    Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&arrayPtr->vars, key);
516
517    if (hPtr == NULL) {
518        PsStore *psPtr = arrayPtr->psPtr;
519        if (psPtr) {
520            char *val = NULL;
521            int len = 0;
522            if ((*psPtr->psGet)(psPtr->psHandle, key, &val, &len) == 0) {
523                tclObj = Tcl_NewStringObj(val, len);
524                (*psPtr->psFree)(val);
525            }
526        }
527        if (!(flags & FLAGS_CREATEVAR) && tclObj == NULL) {
528            return NULL;
529        }
530        if (tclObj == NULL) {
531            tclObj = Tcl_NewObj();
532        }
533        hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, key, &new);
534        Tcl_SetHashValue(hPtr, CreateContainer(arrayPtr, hPtr, tclObj));
535    }
536
537    return (Container*)Tcl_GetHashValue(hPtr);
538}
539
540/*
541 *-----------------------------------------------------------------------------
542 *
543 * ReleaseContainer --
544 *
545 *      Does some post-processing on the used container. This is mostly
546 *      needed when the container has been modified and needs to be
547 *      saved in the bound persistent storage.
548 *
549 * Results:
550 *      A standard Tcl result
551 *
552 * Side effects:
553 *      Persistent storage, if bound, might be modified.
554 *
555 *-----------------------------------------------------------------------------
556 */
557
558static int
559ReleaseContainer(interp, svObj, mode)
560    Tcl_Interp *interp;
561    Container *svObj;
562    int mode;
563{
564    PsStore *psPtr = svObj->arrayPtr->psPtr;
565    int len;
566    char *key, *val;
567
568    switch (mode) {
569    case SV_UNCHANGED: return TCL_OK;
570    case SV_ERROR:     return TCL_ERROR;
571    case SV_CHANGED:
572        if (psPtr) {
573            key = Tcl_GetHashKey(&svObj->arrayPtr->vars, svObj->entryPtr);
574            val = Tcl_GetStringFromObj(svObj->tclObj, &len);
575            if ((*psPtr->psPut)(psPtr->psHandle, key, val, len) == -1) {
576                const char *err = (*psPtr->psError)(psPtr->psHandle);
577                Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1));
578                return TCL_ERROR;
579            }
580        }
581        return TCL_OK;
582    }
583
584    return TCL_ERROR; /* Should never be reached */
585}
586
587/*
588 *-----------------------------------------------------------------------------
589 *
590 * CreateContainer --
591 *
592 *      Creates new shared container holding Tcl object to be stored
593 *      in the shared array
594 *
595 * Results:
596 *      The container pointer.
597 *
598 * Side effects:
599 *      Memory gets allocated.
600 *
601 *-----------------------------------------------------------------------------
602 */
603
604static Container *
605CreateContainer(arrayPtr, entryPtr, tclObj)
606    Array *arrayPtr;
607    Tcl_HashEntry *entryPtr;
608    Tcl_Obj *tclObj;
609{
610    Container *svObj;
611
612    if (arrayPtr->bucketPtr->freeCt == NULL) {
613        SvAllocateContainers(arrayPtr->bucketPtr);
614    }
615
616    svObj = arrayPtr->bucketPtr->freeCt;
617    arrayPtr->bucketPtr->freeCt = svObj->nextPtr;
618
619    svObj->arrayPtr  = arrayPtr;
620    svObj->bucketPtr = arrayPtr->bucketPtr;
621    svObj->tclObj    = tclObj;
622    svObj->entryPtr  = entryPtr;
623    svObj->handlePtr = NULL;
624
625    if (svObj->tclObj) {
626        Tcl_IncrRefCount(svObj->tclObj);
627    }
628
629    return svObj;
630}
631
632/*
633 *-----------------------------------------------------------------------------
634 *
635 * DeleteContainer --
636 *
637 *      Destroys the container and the Tcl object within it. For bound
638 *      shared arrays, the underlying persistent store is updated as well.
639 *
640 * Results:
641 *      None.
642 *
643 * Side effects:
644 *      Memory gets reclaimed. If the shared array was bound to persistent
645 *      storage, it removes the corresponding record.
646 *
647 *-----------------------------------------------------------------------------
648 */
649
650static int
651DeleteContainer(svObj)
652    Container *svObj;
653{
654    if (svObj->tclObj) {
655        Tcl_DecrRefCount(svObj->tclObj);
656    }
657    if (svObj->handlePtr) {
658        Tcl_DeleteHashEntry(svObj->handlePtr);
659    }
660    if (svObj->entryPtr) {
661        PsStore *psPtr = svObj->arrayPtr->psPtr;
662        if (psPtr) {
663            char *key = Tcl_GetHashKey(&svObj->arrayPtr->vars,svObj->entryPtr);
664            if ((*psPtr->psDelete)(psPtr->psHandle, key) == -1) {
665                return TCL_ERROR;
666            }
667        }
668        Tcl_DeleteHashEntry(svObj->entryPtr);
669    }
670
671    svObj->arrayPtr  = NULL;
672    svObj->entryPtr  = NULL;
673    svObj->handlePtr = NULL;
674    svObj->tclObj    = NULL;
675
676    svObj->nextPtr = svObj->bucketPtr->freeCt;
677    svObj->bucketPtr->freeCt = svObj;
678
679    return TCL_OK;
680}
681
682/*
683 *-----------------------------------------------------------------------------
684 *
685 * LockArray --
686 *
687 *      Find (or create) the array structure for shared array and lock it.
688 *      Array structure must be later unlocked with UnlockArray.
689 *
690 * Results:
691 *      TCL_OK or TCL_ERROR if no such array.
692 *
693 * Side effects:
694 *      Sets *arrayPtrPtr with Array pointer or leave error in given interp.
695 *
696 *-----------------------------------------------------------------------------
697 */
698
699static Array *
700LockArray(interp, array, flags)
701    Tcl_Interp *interp;                 /* Interpreter to leave result. */
702    const char *array;                  /* Name of array to lock */
703    int flags;                          /* FLAGS_CREATEARRAY/FLAGS_NOERRMSG*/
704{
705    register const char *p;
706    register unsigned int result;
707    register int i;
708    Bucket *bucketPtr;
709    Array *arrayPtr;
710
711    /*
712     * Compute a hash to map an array to a bucket.
713     */
714
715    p = array;
716    result = 0;
717    while (*p++) {
718        i = *p;
719        result += (result << 3) + i;
720    }
721    i = result % NUMBUCKETS;
722    bucketPtr = &buckets[i];
723
724    /*
725     * Lock the bucket and find the array, or create a new one.
726     * The bucket will be left locked on success.
727     */
728
729    LOCK_BUCKET(bucketPtr); /* Note: no matching unlock below ! */
730    if (flags & FLAGS_CREATEARRAY) {
731        arrayPtr = CreateArray(bucketPtr, array);
732    } else {
733        Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&bucketPtr->arrays, array);
734        if (hPtr == NULL) {
735            UNLOCK_BUCKET(bucketPtr);
736            if (!(flags & FLAGS_NOERRMSG)) {
737                Tcl_AppendResult(interp, "\"", array,
738                                 "\" is not a thread shared array", NULL);
739            }
740            return NULL;
741        }
742        arrayPtr = (Array*)Tcl_GetHashValue(hPtr);
743    }
744
745    return arrayPtr;
746}
747/*
748 *-----------------------------------------------------------------------------
749 *
750 * FlushArray --
751 *
752 *      Unset all keys in an array.
753 *
754 * Results:
755 *      None.
756 *
757 * Side effects:
758 *      Array is cleaned but it's variable hash-hable still lives.
759 *      For bound arrays, the persistent store is updated accordingly.
760 *
761 *-----------------------------------------------------------------------------
762 */
763
764static int
765FlushArray(arrayPtr)
766    Array *arrayPtr;                    /* Name of array to flush */
767{
768    Tcl_HashEntry *hPtr;
769    Tcl_HashSearch search;
770
771    for (hPtr = Tcl_FirstHashEntry(&arrayPtr->vars, &search); hPtr;
772         hPtr = Tcl_NextHashEntry(&search)) {
773        if (DeleteContainer((Container*)Tcl_GetHashValue(hPtr)) != TCL_OK) {
774            return TCL_ERROR;
775        }
776    }
777
778    return TCL_OK;
779}
780
781/*
782 *-----------------------------------------------------------------------------
783 *
784 * CreateArray --
785 *
786 *      Creates new shared array instance.
787 *
788 * Results:
789 *      Pointer to the newly created array
790 *
791 * Side effects:
792 *      Memory gets allocated
793 *
794 *-----------------------------------------------------------------------------
795 */
796
797static Array *
798CreateArray(bucketPtr, arrayName)
799    Bucket *bucketPtr;
800    const char *arrayName;
801{
802    int new;
803    Array *arrayPtr;
804    Tcl_HashEntry *hPtr;
805
806    hPtr = Tcl_CreateHashEntry(&bucketPtr->arrays, arrayName, &new);
807    if (!new) {
808        return (Array*)Tcl_GetHashValue(hPtr);
809    }
810
811    arrayPtr = (Array*)Tcl_Alloc(sizeof(Array));
812    arrayPtr->bucketPtr = bucketPtr;
813    arrayPtr->entryPtr  = hPtr;
814    arrayPtr->psPtr     = NULL;
815    arrayPtr->bindAddr  = NULL;
816
817    Tcl_InitHashTable(&arrayPtr->vars, TCL_STRING_KEYS);
818    Tcl_SetHashValue(hPtr, arrayPtr);
819
820    return arrayPtr;
821}
822
823/*
824 *-----------------------------------------------------------------------------
825 *
826 * DeleteArray --
827 *
828 *      Deletes the shared array.
829 *
830 * Results:
831 *      A standard Tcl result.
832 *
833 * Side effects:
834 *      Memory gets reclaimed.
835 *
836 *-----------------------------------------------------------------------------
837 */
838
839static int
840DeleteArray(arrayPtr)
841    Array *arrayPtr;
842{
843    if (FlushArray(arrayPtr) == -1) {
844        return TCL_ERROR;
845    }
846    if (arrayPtr->psPtr) {
847        PsStore *psPtr = arrayPtr->psPtr;
848        if ((*psPtr->psClose)(psPtr->psHandle) == -1) {
849            return TCL_ERROR;
850        }
851        Tcl_Free((char*)arrayPtr->psPtr), arrayPtr->psPtr = NULL;
852    }
853    if (arrayPtr->bindAddr) {
854        Tcl_Free(arrayPtr->bindAddr);
855    }
856    if (arrayPtr->entryPtr) {
857        Tcl_DeleteHashEntry(arrayPtr->entryPtr);
858    }
859
860    Tcl_DeleteHashTable(&arrayPtr->vars);
861    Tcl_Free((char*)arrayPtr);
862
863    return TCL_OK;
864}
865
866/*
867 *-----------------------------------------------------------------------------
868 *
869 * SvAllocateContainers --
870 *
871 *      Any similarity with the Tcl AllocateFreeObj function is purely
872 *      coincidental... Just joking; this is (almost) 100% copy of it! :-)
873 *
874 * Results:
875 *      None.
876 *
877 * Side effects:
878 *      Allocates memory for many containers at the same time
879 *
880 *-----------------------------------------------------------------------------
881 */
882
883static void
884SvAllocateContainers(bucketPtr)
885    Bucket *bucketPtr;
886{
887    Container tmp[2];
888    size_t objSizePlusPadding = (size_t)(((char*)(tmp+1))-(char*)tmp);
889    size_t bytesToAlloc = (OBJS_TO_ALLOC_EACH_TIME * objSizePlusPadding);
890    char *basePtr;
891    register Container *prevPtr = NULL, *objPtr = NULL;
892    register int i;
893
894    basePtr = (char*)Tcl_Alloc(bytesToAlloc);
895    memset(basePtr, 0, bytesToAlloc);
896
897    objPtr = (Container*)basePtr;
898    objPtr->chunkAddr = basePtr; /* Mark chunk address for reclaim */
899
900    for (i = 0; i < OBJS_TO_ALLOC_EACH_TIME; i++) {
901        objPtr->nextPtr = prevPtr;
902        prevPtr = objPtr;
903        objPtr = (Container*)(((char*)objPtr) + objSizePlusPadding);
904    }
905    bucketPtr->freeCt = prevPtr;
906}
907
908#ifdef SV_FINALIZE
909/*
910 *-----------------------------------------------------------------------------
911 *
912 * SvFinalizeContainers --
913 *
914 *    Reclaim memory for free object containers per bucket.
915 *
916 * Results:
917 *    None.
918 *
919 * Side effects:
920 *    Memory gets reclaimed
921 *
922 *-----------------------------------------------------------------------------
923 */
924
925static void
926SvFinalizeContainers(bucketPtr)
927    Bucket *bucketPtr;
928{
929   Container *tmpPtr, *objPtr = bucketPtr->freeCt;
930
931    while (objPtr) {
932        if (objPtr->chunkAddr == (char*)objPtr) {
933            tmpPtr = objPtr->nextPtr;
934            Tcl_Free((char*)objPtr);
935            objPtr = tmpPtr;
936        } else {
937            objPtr = objPtr->nextPtr;
938        }
939    }
940}
941#endif /* SV_FINALIZE */
942
943/*
944 *-----------------------------------------------------------------------------
945 *
946 * Sv_DuplicateObj --
947 *
948 *  Create and return a new object that is (mostly) a duplicate of the
949 *  argument object. We take care that the duplicate object is either
950 *  a proper object copy, i.e. w/o hidden references to original object
951 *  elements or a plain string object, i.e one w/o internal representation.
952 *
953 *  Decision about wether to produce a real duplicate or a string object
954 *  is done as follows:
955 *
956 *     1) Scalar Tcl object types are properly copied by default;
957 *        these include: boolean, int double, string and byteArray types.
958 *     2) Object registered with Sv_RegisterObjType are duplicated
959 *        using custom duplicator function which is guaranteed to
960 *        produce a proper deep copy of the object in question.
961 *     3) All other object types are stringified; these include
962 *        miscelaneous Tcl objects (cmdName, nsName, bytecode, etc, etc)
963 *        and all user-defined objects.
964 *
965 * Results:
966 *      The return value is a pointer to a newly created Tcl_Obj. This
967 *      object has reference count 0 and the same type, if any, as the
968 *      source object objPtr. Also:
969 *
970 *        1) If the source object has a valid string rep, we copy it;
971 *           otherwise, the new string rep is marked invalid.
972 *        2) If the source object has an internal representation (i.e. its
973 *           typePtr is non-NULL), the new object's internal rep is set to
974 *           a copy; otherwise the new internal rep is marked invalid.
975 *
976 * Side effects:
977 *  Some object may, when copied, loose their type, i.e. will become
978 *  just plain string objects.
979 *
980 *-----------------------------------------------------------------------------
981 */
982
983Tcl_Obj *
984Sv_DuplicateObj(objPtr)
985    register Tcl_Obj *objPtr;        /* The object to duplicate. */
986{
987    register Tcl_Obj *dupPtr = Tcl_NewObj();
988
989    /*
990     * Handle the internal rep
991     */
992
993    if (objPtr->typePtr != NULL) {
994        if (objPtr->typePtr->dupIntRepProc == NULL) {
995            dupPtr->internalRep = objPtr->internalRep;
996            dupPtr->typePtr = objPtr->typePtr;
997            Tcl_InvalidateStringRep(dupPtr);
998        } else {
999            if (   objPtr->typePtr == booleanObjTypePtr    \
1000                || objPtr->typePtr == byteArrayObjTypePtr  \
1001                || objPtr->typePtr == doubleObjTypePtr     \
1002                || objPtr->typePtr == intObjTypePtr        \
1003                || objPtr->typePtr == stringObjTypePtr) {
1004               /*
1005                * Cover all "safe" obj types (see header comment)
1006                */
1007              (*objPtr->typePtr->dupIntRepProc)(objPtr, dupPtr);
1008              Tcl_InvalidateStringRep(dupPtr);
1009            } else {
1010                int found = 0;
1011                register RegType *regPtr;
1012               /*
1013                * Cover special registered types. Assume not
1014                * very many of those, so this sequential walk
1015                * should be fast enough.
1016                */
1017                for (regPtr = regType; regPtr; regPtr = regPtr->nextPtr) {
1018                    if (objPtr->typePtr == regPtr->typePtr) {
1019                        (*regPtr->dupIntRepProc)(objPtr, dupPtr);
1020                        Tcl_InvalidateStringRep(dupPtr);
1021                        found = 1;
1022                        break;
1023                    }
1024                }
1025               /*
1026                * Assure at least string rep of the source
1027                * is present, which will be copied below.
1028                */
1029                if (found == 0 && objPtr->bytes == NULL
1030                    && objPtr->typePtr->updateStringProc != NULL) {
1031                    (*objPtr->typePtr->updateStringProc)(objPtr);
1032                }
1033            }
1034        }
1035    }
1036
1037    /*
1038     * Handle the string rep
1039     */
1040
1041    if (objPtr->bytes == NULL) {
1042        dupPtr->bytes = NULL;
1043    } else if (objPtr->bytes != Sv_tclEmptyStringRep) {
1044        /* A copy of TclInitStringRep macro */
1045        dupPtr->bytes = (char*)Tcl_Alloc((unsigned)objPtr->length + 1);
1046        if (objPtr->length > 0) {
1047            memcpy((void*)dupPtr->bytes,(void*)objPtr->bytes,
1048                   (unsigned)objPtr->length);
1049        }
1050        dupPtr->length = objPtr->length;
1051        dupPtr->bytes[objPtr->length] = '\0';
1052    }
1053
1054    return dupPtr;
1055}
1056
1057/*
1058 *-----------------------------------------------------------------------------
1059 *
1060 * SvObjDispatchObjCmd --
1061 *
1062 *      The method command for dispatching sub-commands of the shared
1063 *      object.
1064 *
1065 * Results:
1066 *      A standard Tcl result.
1067 *
1068 * Side effects:
1069 *      Depends on the dispatched command
1070 *
1071 *-----------------------------------------------------------------------------
1072 */
1073
1074static int
1075SvObjDispatchObjCmd(arg, interp, objc, objv)
1076    ClientData arg;                     /* Just passed to the command. */
1077    Tcl_Interp *interp;                 /* Current interpreter. */
1078    int objc;                           /* Number of arguments. */
1079    Tcl_Obj *const objv[];              /* Argument objects. */
1080{
1081    const char *cmdName;
1082    SvCmdInfo *cmdPtr;
1083
1084    if (objc < 2) {
1085        Tcl_WrongNumArgs(interp, 1, objv, "args");
1086        return TCL_ERROR;
1087    }
1088
1089    cmdName = Tcl_GetString(objv[1]);
1090
1091    /*
1092     * Do simple linear search. We may later replace this list
1093     * with the hash table to gain speed. Currently, the list
1094     * of registered commands is so small, so this will work
1095     * fast enough.
1096     */
1097
1098    for (cmdPtr = svCmdInfo; cmdPtr; cmdPtr = cmdPtr->nextPtr) {
1099        if (!strcmp(cmdPtr->name, cmdName)) {
1100            return (*cmdPtr->objProcPtr)(arg, interp, objc, objv);
1101        }
1102    }
1103
1104    Tcl_AppendResult(interp, "invalid command name \"", cmdName, "\"", NULL);
1105    return TCL_ERROR;
1106}
1107
1108/*
1109 *-----------------------------------------------------------------------------
1110 *
1111 * SvObjObjCmd --
1112 *
1113 *      Creates the object command for a shared array.
1114 *
1115 * Results:
1116 *      A standard Tcl result.
1117 *
1118 * Side effects:
1119 *      New Tcl command gets created.
1120 *
1121 *-----------------------------------------------------------------------------
1122 */
1123
1124static int
1125SvObjObjCmd(dummy, interp, objc, objv)
1126    ClientData dummy;                   /* Not used. */
1127    Tcl_Interp *interp;                 /* Current interpreter. */
1128    int objc;                           /* Number of arguments. */
1129    Tcl_Obj *const objv[];              /* Argument objects. */
1130{
1131    int new, off, ret, flg;
1132    char buf[128];
1133    Tcl_Obj *val = NULL;
1134    Container *svObj = NULL;
1135
1136    /*
1137     * Syntax: sv::object array key ?var?
1138     */
1139
1140    ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0);
1141    switch (ret) {
1142    case TCL_BREAK: /* Shared array was not found */
1143        if ((objc - off)) {
1144            val = objv[off];
1145        }
1146        Tcl_ResetResult(interp);
1147        flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR;
1148        ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg);
1149        if (ret != TCL_OK) {
1150            return TCL_ERROR;
1151        }
1152        Tcl_DecrRefCount(svObj->tclObj);
1153        svObj->tclObj = Sv_DuplicateObj(val ? val : Tcl_NewObj());
1154        Tcl_IncrRefCount(svObj->tclObj);
1155        break;
1156    case TCL_ERROR:
1157        return TCL_ERROR;
1158    }
1159
1160    if (svObj->handlePtr == NULL) {
1161        Tcl_HashTable *handles = &svObj->arrayPtr->bucketPtr->handles;
1162        svObj->handlePtr = Tcl_CreateHashEntry(handles, (char*)svObj, &new);
1163    }
1164
1165    /*
1166     * Format the command name
1167     */
1168
1169    sprintf(buf, "::%p", (int*)svObj);
1170    Tcl_CreateObjCommand(interp, buf, SvObjDispatchObjCmd, (int*)svObj, NULL);
1171    Tcl_ResetResult(interp);
1172    Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1));
1173
1174    return Sv_PutContainer(interp, svObj, SV_UNCHANGED);
1175}
1176
1177/*
1178 *-----------------------------------------------------------------------------
1179 *
1180 * SvArrayObjCmd --
1181 *
1182 *      This procedure is invoked to process the "tsv::array" command.
1183 *      See the user documentation for details on what it does.
1184 *
1185 * Results:
1186 *      A standard Tcl result.
1187 *
1188 * Side effects:
1189 *      See the user documentation.
1190 *
1191 *-----------------------------------------------------------------------------
1192 */
1193
1194static int
1195SvArrayObjCmd(arg, interp, objc, objv)
1196    ClientData arg;                     /* Pointer to object container. */
1197    Tcl_Interp *interp;                 /* Current interpreter. */
1198    int objc;                           /* Number of arguments. */
1199    Tcl_Obj *const objv[];              /* Argument objects. */
1200{
1201    int i, argx = 0, lobjc = 0, index, ret = TCL_OK;
1202    const char *arrayName = NULL;
1203    Array *arrayPtr = NULL;
1204    Tcl_Obj **lobjv = NULL;
1205    Container *svObj, *elObj = NULL;
1206
1207    static const char *opts[] = {
1208        "set",  "reset", "get", "names", "size", "exists", "isbound",
1209        "bind", "unbind", NULL
1210    };
1211    enum options {
1212        ASET,   ARESET,  AGET,  ANAMES,  ASIZE,  AEXISTS, AISBOUND,
1213        ABIND,  AUNBIND
1214    };
1215
1216    svObj = (Container*)arg;
1217
1218    if (objc < 3) {
1219        Tcl_WrongNumArgs(interp, 1, objv, "option array");
1220        return TCL_ERROR;
1221    }
1222
1223    arrayName = Tcl_GetString(objv[2]);
1224    arrayPtr  = LockArray(interp, arrayName, FLAGS_NOERRMSG);
1225
1226    if (objc > 3) {
1227        argx = 3;
1228    }
1229
1230    Tcl_ResetResult(interp);
1231
1232    if (Tcl_GetIndexFromObj(interp,objv[1],opts,"option",0,&index) != TCL_OK) {
1233        ret = TCL_ERROR;
1234
1235    } else if (index == AEXISTS) {
1236        Tcl_SetBooleanObj(Tcl_GetObjResult(interp), arrayPtr ? 1 : 0);
1237
1238    } else if (index == AISBOUND) {
1239        if (arrayPtr == NULL) {
1240            Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0);
1241        } else {
1242            Tcl_SetBooleanObj(Tcl_GetObjResult(interp), arrayPtr->psPtr ? 1:0);
1243        }
1244
1245    } else if (index == ASIZE) {
1246        if (arrayPtr == NULL) {
1247            Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
1248        } else {
1249            Tcl_SetLongObj(Tcl_GetObjResult(interp),arrayPtr->vars.numEntries);
1250        }
1251
1252    } else if (index == ASET || index == ARESET) {
1253        if (argx == (objc - 1)) {
1254            if (argx && Tcl_ListObjGetElements(interp, objv[argx], &lobjc,
1255                    &lobjv) != TCL_OK) {
1256                ret = TCL_ERROR;
1257                goto cmdExit;
1258            }
1259        } else {
1260            lobjc = objc - 3;
1261            lobjv = (Tcl_Obj**)objv + 3;
1262        }
1263        if (lobjc & 1) {
1264            Tcl_AppendResult(interp, "list must have an even number"
1265                    " of elements", NULL);
1266            ret = TCL_ERROR;
1267            goto cmdExit;
1268        }
1269        if (arrayPtr == NULL) {
1270            arrayPtr = LockArray(interp, arrayName, FLAGS_CREATEARRAY);
1271        }
1272        if (index == ARESET) {
1273            ret = FlushArray(arrayPtr);
1274            if (ret != TCL_OK) {
1275                if (arrayPtr->psPtr) {
1276                    PsStore *psPtr = arrayPtr->psPtr;
1277                    char *err = (*psPtr->psError)(psPtr->psHandle);
1278                    Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1));
1279                }
1280                goto cmdExit;
1281            }
1282        }
1283        for (i = 0; i < lobjc; i += 2) {
1284            const char *key = Tcl_GetString(lobjv[i]);
1285            elObj = AcquireContainer(arrayPtr, key, FLAGS_CREATEVAR);
1286            Tcl_DecrRefCount(elObj->tclObj);
1287            elObj->tclObj = Sv_DuplicateObj(lobjv[i+1]);
1288            Tcl_IncrRefCount(elObj->tclObj);
1289            if (ReleaseContainer(interp, elObj, SV_CHANGED) != TCL_OK) {
1290                ret = TCL_ERROR;
1291                goto cmdExit;
1292            }
1293        }
1294
1295    } else if (index == AGET || index == ANAMES) {
1296        if (arrayPtr) {
1297            Tcl_HashSearch search;
1298            Tcl_Obj *resObj = Tcl_NewListObj(0, NULL);
1299            const char *pattern = (argx == 0) ? NULL : Tcl_GetString(objv[argx]);
1300            Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&arrayPtr->vars,&search);
1301            while (hPtr) {
1302                char *key = Tcl_GetHashKey(&arrayPtr->vars, hPtr);
1303                if (pattern == NULL || Tcl_StringMatch(key, pattern)) {
1304                    Tcl_ListObjAppendElement(interp, resObj,
1305                            Tcl_NewStringObj(key, -1));
1306                    if (index == AGET) {
1307                        elObj = (Container*)Tcl_GetHashValue(hPtr);
1308                        Tcl_ListObjAppendElement(interp, resObj,
1309                                Sv_DuplicateObj(elObj->tclObj));
1310                    }
1311                }
1312                hPtr = Tcl_NextHashEntry(&search);
1313            }
1314            Tcl_SetObjResult(interp, resObj);
1315        }
1316
1317    } else if (index == ABIND) {
1318
1319        /*
1320         * This is more complex operation, requiring some clarification.
1321         *
1322         * When binding an already existing array, we walk the array
1323         * first and store all key/value pairs found there in the
1324         * persistent storage. Then we proceed with the below.
1325         *
1326         * When binding an non-existent array, we open the persistent
1327         * storage and cache all key/value pairs found there into tne
1328         * newly created shared array.
1329         */
1330
1331        PsStore *psPtr;
1332        int len;
1333        char *psurl, *key = NULL, *val = NULL;
1334
1335        if (objc < 4) {
1336            Tcl_WrongNumArgs(interp, 2, objv, "array handle");
1337            ret = TCL_ERROR;
1338            goto cmdExit;
1339        }
1340
1341        if (arrayPtr && arrayPtr->psPtr) {
1342            Tcl_AppendResult(interp, "array is already bound", NULL);
1343            ret = TCL_ERROR;
1344            goto cmdExit;
1345        }
1346
1347        psurl = Tcl_GetStringFromObj(objv[3], &len);
1348        psPtr = GetPsStore(psurl);
1349
1350        if (psPtr == NULL) {
1351            Tcl_AppendResult(interp, "can't open persistent storage on \"",
1352                             psurl, "\"", NULL);
1353            ret = TCL_ERROR;
1354            goto cmdExit;
1355        }
1356        if (arrayPtr) {
1357            Tcl_HashSearch search;
1358            Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&arrayPtr->vars,&search);
1359            arrayPtr->psPtr = psPtr;
1360            arrayPtr->bindAddr = strcpy(Tcl_Alloc(len+1), psurl);
1361            while (hPtr) {
1362                svObj = Tcl_GetHashValue(hPtr);
1363                if (ReleaseContainer(interp, svObj, SV_CHANGED) != TCL_OK) {
1364                    ret = TCL_ERROR;
1365                    goto cmdExit;
1366                }
1367                hPtr = Tcl_NextHashEntry(&search);
1368            }
1369        } else {
1370            arrayPtr = LockArray(interp, arrayName, FLAGS_CREATEARRAY);
1371            arrayPtr->psPtr = psPtr;
1372            arrayPtr->bindAddr = strcpy(Tcl_Alloc(len+1), psurl);
1373        }
1374        if (!(*psPtr->psFirst)(psPtr->psHandle, &key, &val, &len)) {
1375            do {
1376                (*psPtr->psFree)(val); /* What a waste! */
1377                AcquireContainer(arrayPtr, key, FLAGS_CREATEVAR);
1378            } while (!(*psPtr->psNext)(psPtr->psHandle, &key, &val, &len));
1379        }
1380
1381    } else if (index == AUNBIND) {
1382        if (arrayPtr && arrayPtr->psPtr) {
1383            PsStore *psPtr = arrayPtr->psPtr;
1384            if ((*psPtr->psClose)(psPtr->psHandle) == -1) {
1385                char *err = (*psPtr->psError)(psPtr->psHandle);
1386                Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1));
1387                ret = TCL_ERROR;
1388                goto cmdExit;
1389            }
1390            Tcl_Free((char*)arrayPtr->psPtr), arrayPtr->psPtr = NULL;
1391        } else {
1392            Tcl_AppendResult(interp, "shared variable is not bound", NULL);
1393            ret = TCL_ERROR;
1394            goto cmdExit;
1395        }
1396    }
1397
1398 cmdExit:
1399    if (arrayPtr) {
1400        UnlockArray(arrayPtr);
1401    }
1402
1403    return ret;
1404}
1405
1406/*
1407 *-----------------------------------------------------------------------------
1408 *
1409 * SvUnsetObjCmd --
1410 *
1411 *      This procedure is invoked to process the "tsv::unset" command.
1412 *      See the user documentation for details on what it does.
1413 *
1414 * Results:
1415 *      A standard Tcl result.
1416 *
1417 * Side effects:
1418 *      See the user documentation.
1419 *
1420 *-----------------------------------------------------------------------------
1421 */
1422
1423static int
1424SvUnsetObjCmd(dummy, interp, objc, objv)
1425    ClientData dummy;                   /* Not used. */
1426    Tcl_Interp *interp;                 /* Current interpreter. */
1427    int objc;                           /* Number of arguments. */
1428    Tcl_Obj *const objv[];              /* Argument objects. */
1429{
1430    int ii;
1431    const char *arrayName;
1432    Array *arrayPtr;
1433
1434    if (objc < 2) {
1435        Tcl_WrongNumArgs(interp, 1, objv, "array ?key ...?");
1436        return TCL_ERROR;
1437    }
1438
1439    arrayName = Tcl_GetString(objv[1]);
1440    arrayPtr  = LockArray(interp, arrayName, 0);
1441
1442    if (arrayPtr == NULL) {
1443        return TCL_ERROR;
1444    }
1445    if (objc == 2) {
1446        UnlockArray(arrayPtr);
1447        if (DeleteArray(arrayPtr) != TCL_OK) {
1448            return TCL_ERROR;
1449        }
1450    } else {
1451        for (ii = 2; ii < objc; ii++) {
1452            const char *key = Tcl_GetString(objv[ii]);
1453            Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&arrayPtr->vars, key);
1454            if (hPtr) {
1455                if (DeleteContainer((Container*)Tcl_GetHashValue(hPtr))
1456                    != TCL_OK) {
1457                    UnlockArray(arrayPtr);
1458                    return TCL_ERROR;
1459                }
1460            } else {
1461                UnlockArray(arrayPtr);
1462                Tcl_AppendResult(interp,"no key ",arrayName,"(",key,")",NULL);
1463                return TCL_ERROR;
1464            }
1465        }
1466        UnlockArray(arrayPtr);
1467    }
1468
1469    return TCL_OK;
1470}
1471
1472/*
1473 *-----------------------------------------------------------------------------
1474 *
1475 * SvNamesObjCmd --
1476 *
1477 *      This procedure is invoked to process the "tsv::names" command.
1478 *      See the user documentation for details on what it does.
1479 *
1480 * Results:
1481 *      A standard Tcl result.
1482 *
1483 * Side effects:
1484 *      See the user documentation.
1485 *
1486 *-----------------------------------------------------------------------------
1487 */
1488
1489static int
1490SvNamesObjCmd(dummy, interp, objc, objv)
1491    ClientData dummy;                   /* Not used. */
1492    Tcl_Interp *interp;                 /* Current interpreter. */
1493    int objc;                           /* Number of arguments. */
1494    Tcl_Obj *const objv[];              /* Argument objects. */
1495{
1496    int i, len;
1497    const char *pattern = NULL;
1498    Tcl_HashEntry *hPtr;
1499    Tcl_HashSearch search;
1500    Tcl_Obj *resObj;
1501
1502    if (objc > 2) {
1503        Tcl_WrongNumArgs(interp, 1, objv, "?pattern?");
1504        return TCL_ERROR;
1505    }
1506    if (objc == 2) {
1507        pattern = Tcl_GetStringFromObj(objv[1], &len);
1508    }
1509
1510    resObj = Tcl_NewListObj(0, NULL);
1511
1512    for (i = 0; i < NUMBUCKETS; i++) {
1513        Bucket *bucketPtr = &buckets[i];
1514        LOCK_BUCKET(bucketPtr);
1515        hPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search);
1516        while (hPtr) {
1517            char *key = Tcl_GetHashKey(&bucketPtr->arrays, hPtr);
1518#ifdef HIDE_DOTNAMES
1519            if (*key != '.' /* Hide .<name> arrays */ &&
1520#else
1521            if (1 &&
1522#endif
1523                (pattern == NULL || Tcl_StringMatch(key, pattern))) {
1524                Tcl_ListObjAppendElement(interp, resObj,
1525                        Tcl_NewStringObj(key, -1));
1526            }
1527            hPtr = Tcl_NextHashEntry(&search);
1528        }
1529        UNLOCK_BUCKET(bucketPtr);
1530    }
1531
1532    Tcl_SetObjResult(interp, resObj);
1533
1534    return TCL_OK;
1535}
1536
1537/*
1538 *-----------------------------------------------------------------------------
1539 *
1540 * SvGetObjCmd --
1541 *
1542 *      This procedure is invoked to process "tsv::get" command.
1543 *      See the user documentation for details on what it does.
1544 *
1545 * Results:
1546 *      A standard Tcl result.
1547 *
1548 * Side effects:
1549 *      See the user documentation.
1550 *
1551 *-----------------------------------------------------------------------------
1552 */
1553
1554static int
1555SvGetObjCmd(arg, interp, objc, objv)
1556    ClientData arg;                     /* Pointer to object container. */
1557    Tcl_Interp *interp;                 /* Current interpreter. */
1558    int objc;                           /* Number of arguments. */
1559    Tcl_Obj *const objv[];              /* Argument objects. */
1560{
1561    int off, ret;
1562    Tcl_Obj *res;
1563    Container *svObj = (Container*)arg;
1564
1565    /*
1566     * Syntax:
1567     *          tsv::get array key ?var?
1568     *          $object get ?var?
1569     */
1570
1571    ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0);
1572    switch (ret) {
1573    case TCL_BREAK:
1574        if ((objc - off) == 0) {
1575            return TCL_ERROR;
1576        } else {
1577            Tcl_ResetResult(interp);
1578            Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
1579            return TCL_OK;
1580        }
1581    case TCL_ERROR:
1582        return TCL_ERROR;
1583    }
1584
1585    res = Sv_DuplicateObj(svObj->tclObj);
1586
1587    if ((objc - off) == 0) {
1588        Tcl_SetObjResult(interp, res);
1589    } else {
1590        if (Tcl_ObjSetVar2(interp, objv[off], NULL, res, 0) == NULL) {
1591            Tcl_DecrRefCount(res);
1592            goto cmd_err;
1593        }
1594        Tcl_ResetResult(interp);
1595        Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
1596    }
1597
1598    return Sv_PutContainer(interp, svObj, SV_UNCHANGED);
1599
1600 cmd_err:
1601    return Sv_PutContainer(interp, svObj, SV_ERROR);
1602}
1603
1604/*
1605 *-----------------------------------------------------------------------------
1606 *
1607 * SvExistsObjCmd --
1608 *
1609 *      This procedure is invoked to process "tsv::exists" command.
1610 *      See the user documentation for details on what it does.
1611 *
1612 * Results:
1613 *      A standard Tcl result.
1614 *
1615 * Side effects:
1616 *      See the user documentation.
1617 *
1618 *-----------------------------------------------------------------------------
1619 */
1620
1621static int
1622SvExistsObjCmd(arg, interp, objc, objv)
1623    ClientData arg;                     /* Pointer to object container. */
1624    Tcl_Interp *interp;                 /* Current interpreter. */
1625    int objc;                           /* Number of arguments. */
1626    Tcl_Obj *const objv[];              /* Argument objects. */
1627{
1628    int off, ret;
1629    Container *svObj = (Container*)arg;
1630
1631    /*
1632     * Syntax:
1633     *          tsv::exists array key
1634     *          $object exists
1635     */
1636
1637    ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0);
1638    switch (ret) {
1639    case TCL_BREAK: /* Array/key not found */
1640        Tcl_ResetResult(interp);
1641        Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0);
1642        return TCL_OK;
1643    case TCL_ERROR:
1644        return TCL_ERROR;
1645    }
1646
1647    Tcl_ResetResult(interp);
1648    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 1);
1649
1650    return Sv_PutContainer(interp, svObj, SV_UNCHANGED);
1651}
1652
1653/*
1654 *-----------------------------------------------------------------------------
1655 *
1656 * SvSetObjCmd --
1657 *
1658 *      This procedure is invoked to process the "tsv::set" command.
1659 *      See the user documentation for details on what it does.
1660 *
1661 * Results:
1662 *      A standard Tcl result.
1663 *
1664 * Side effects:
1665 *      See the user documentation.
1666 *
1667 *-----------------------------------------------------------------------------
1668 */
1669
1670static int
1671SvSetObjCmd(arg, interp, objc, objv)
1672    ClientData arg;                     /* Pointer to object container */
1673    Tcl_Interp *interp;                 /* Current interpreter. */
1674    int objc;                           /* Number of arguments. */
1675    Tcl_Obj *const objv[];              /* Argument objects. */
1676{
1677    int ret, off, flg, mode;
1678    Tcl_Obj *val;
1679    Container *svObj = (Container*)arg;
1680
1681    /*
1682     * Syntax:
1683     *          tsv::set array key ?value?
1684     *          $object set ?value?
1685     */
1686
1687    ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0);
1688    switch (ret) {
1689    case TCL_BREAK:
1690        if ((objc - off) == 0) {
1691            return TCL_ERROR;
1692        } else {
1693            Tcl_ResetResult(interp);
1694            flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR;
1695            ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg);
1696            if (ret != TCL_OK) {
1697                return TCL_ERROR;
1698            }
1699        }
1700        break;
1701    case TCL_ERROR:
1702        return TCL_ERROR;
1703    }
1704    if ((objc - off)) {
1705        val = objv[off];
1706        Tcl_DecrRefCount(svObj->tclObj);
1707        svObj->tclObj = Sv_DuplicateObj(val);
1708        Tcl_IncrRefCount(svObj->tclObj);
1709        mode = SV_CHANGED;
1710    } else {
1711        val = Sv_DuplicateObj(svObj->tclObj);
1712        mode = SV_UNCHANGED;
1713    }
1714
1715    Tcl_SetObjResult(interp, val);
1716
1717    return Sv_PutContainer(interp, svObj, mode);
1718}
1719
1720/*
1721 *-----------------------------------------------------------------------------
1722 *
1723 * SvIncrObjCmd --
1724 *
1725 *      This procedure is invoked to process the "tsv::incr" command.
1726 *      See the user documentation for details on what it does.
1727 *
1728 * Results:
1729 *      A standard Tcl result.
1730 *
1731 * Side effects:
1732 *      See the user documentation.
1733 *
1734 *-----------------------------------------------------------------------------
1735 */
1736
1737static int
1738SvIncrObjCmd(arg, interp, objc, objv)
1739    ClientData arg;                     /* Pointer to object container */
1740    Tcl_Interp *interp;                 /* Current interpreter. */
1741    int objc;                           /* Number of arguments. */
1742    Tcl_Obj *const objv[];              /* Argument objects. */
1743{
1744    int off, ret, flg, new = 0;
1745    long incrValue = 1, currValue = 0;
1746    Container *svObj = (Container*)arg;
1747
1748    /*
1749     * Syntax:
1750     *          tsv::incr array key ?increment?
1751     *          $object incr ?increment?
1752     */
1753
1754    ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0);
1755    if (ret != TCL_OK) {
1756        if (ret != TCL_BREAK) {
1757            return TCL_ERROR;
1758        }
1759        flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR;
1760        Tcl_ResetResult(interp);
1761        ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg);
1762        if (ret != TCL_OK) {
1763            return TCL_ERROR;
1764        }
1765        new = 1;
1766    }
1767    if ((objc - off)) {
1768        ret = Tcl_GetLongFromObj(interp, objv[off], &incrValue);
1769        if (ret != TCL_OK) {
1770            goto cmd_err;
1771        }
1772    }
1773    if (new) {
1774        currValue = 0;
1775    } else {
1776        ret = Tcl_GetLongFromObj(interp, svObj->tclObj, &currValue);
1777        if (ret != TCL_OK) {
1778            goto cmd_err;
1779        }
1780    }
1781
1782    incrValue += currValue;
1783    Tcl_SetLongObj(svObj->tclObj, incrValue);
1784    Tcl_ResetResult(interp);
1785    Tcl_SetLongObj(Tcl_GetObjResult(interp), incrValue);
1786
1787    return Sv_PutContainer(interp, svObj, SV_CHANGED);
1788
1789 cmd_err:
1790    return Sv_PutContainer(interp, svObj, SV_ERROR);
1791}
1792
1793/*
1794 *-----------------------------------------------------------------------------
1795 *
1796 * SvAppendObjCmd --
1797 *
1798 *      This procedure is invoked to process the "tsv::append" command.
1799 *      See the user documentation for details on what it does.
1800 *
1801 * Results:
1802 *      A standard Tcl result.
1803 *
1804 * Side effects:
1805 *      See the user documentation.
1806 *
1807 *-----------------------------------------------------------------------------
1808 */
1809
1810static int
1811SvAppendObjCmd(arg, interp, objc, objv)
1812    ClientData arg;                     /* Pointer to object container */
1813    Tcl_Interp *interp;                 /* Current interpreter. */
1814    int objc;                           /* Number of arguments. */
1815    Tcl_Obj *const objv[];              /* Argument objects. */
1816{
1817    int i, off, flg, ret;
1818    Container *svObj = (Container*)arg;
1819
1820    /*
1821     * Syntax:
1822     *          tsv::append array key value ?value ...?
1823     *          $object append value ?value ...?
1824     */
1825
1826    flg = FLAGS_CREATEARRAY | FLAGS_CREATEVAR;
1827    ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, flg);
1828    if (ret != TCL_OK) {
1829        return TCL_ERROR;
1830    }
1831    if ((objc - off) < 1) {
1832        Tcl_WrongNumArgs(interp, off, objv, "value ?value ...?");
1833        goto cmd_err;
1834    }
1835    for (i = off; i < objc; ++i) {
1836        Tcl_AppendObjToObj(svObj->tclObj, Sv_DuplicateObj(objv[i]));
1837    }
1838
1839    Tcl_SetObjResult(interp, Sv_DuplicateObj(svObj->tclObj));
1840
1841    return Sv_PutContainer(interp, svObj, SV_CHANGED);
1842
1843 cmd_err:
1844    return Sv_PutContainer(interp, svObj, SV_ERROR);
1845}
1846
1847/*
1848 *-----------------------------------------------------------------------------
1849 *
1850 * SvPopObjCmd --
1851 *
1852 *      This procedure is invoked to process "tsv::pop" command.
1853 *      See the user documentation for details on what it does.
1854 *
1855 * Results:
1856 *      A standard Tcl result.
1857 *
1858 * Side effects:
1859 *      See the user documentation.
1860 *
1861 *-----------------------------------------------------------------------------
1862 */
1863
1864static int
1865SvPopObjCmd(arg, interp, objc, objv)
1866    ClientData arg;                     /* Pointer to object container */
1867    Tcl_Interp *interp;                 /* Current interpreter. */
1868    int objc;                           /* Number of arguments. */
1869    Tcl_Obj *const objv[];              /* Argument objects. */
1870{
1871    int ret, off;
1872    Tcl_Obj *retObj;
1873    Array *arrayPtr = NULL;
1874    Container *svObj = (Container*)arg;
1875
1876    /*
1877     * Syntax:
1878     *          tsv::pop array key ?var?
1879     *          $object pop ?var?
1880     *
1881     * Note: the object command will run into error next time !
1882     */
1883
1884    ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0);
1885    switch (ret) {
1886    case TCL_BREAK:
1887        if ((objc - off) == 0) {
1888            return TCL_ERROR;
1889        } else {
1890            Tcl_ResetResult(interp);
1891            Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
1892            return TCL_OK;
1893        }
1894    case TCL_ERROR:
1895        return TCL_ERROR;
1896    }
1897
1898    arrayPtr = svObj->arrayPtr;
1899
1900    retObj = svObj->tclObj;
1901    svObj->tclObj = NULL;
1902
1903    if (DeleteContainer(svObj) != TCL_OK) {
1904        if (svObj->arrayPtr->psPtr) {
1905            PsStore *psPtr = svObj->arrayPtr->psPtr;
1906            char *err = (*psPtr->psError)(psPtr->psHandle);
1907            Tcl_SetObjResult(interp, Tcl_NewStringObj(err,-1));
1908        }
1909        ret = TCL_ERROR;
1910        goto cmd_exit;
1911    }
1912
1913    if ((objc - off) == 0) {
1914        Tcl_SetObjResult(interp, retObj);
1915    } else {
1916        if (Tcl_ObjSetVar2(interp, objv[off], NULL, retObj, 0) == NULL) {
1917            ret = TCL_ERROR;
1918            goto cmd_exit;
1919        }
1920        Tcl_ResetResult(interp);
1921        Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
1922    }
1923
1924  cmd_exit:
1925    Tcl_DecrRefCount(retObj);
1926    UnlockArray(arrayPtr);
1927
1928    return ret;
1929}
1930
1931/*
1932 *-----------------------------------------------------------------------------
1933 *
1934 * SvMoveObjCmd --
1935 *
1936 *      This procedure is invoked to process the "tsv::move" command.
1937 *      See the user documentation for details on what it does.
1938 *
1939 *
1940 * Results:
1941 *      A standard Tcl result.
1942 *
1943 * Side effects:
1944 *      See the user documentation.
1945 *
1946 *-----------------------------------------------------------------------------
1947 */
1948
1949static int
1950SvMoveObjCmd(arg, interp, objc, objv)
1951    ClientData arg;                     /* Pointer to object container. */
1952    Tcl_Interp *interp;                 /* Current interpreter. */
1953    int objc;                           /* Number of arguments. */
1954    Tcl_Obj *const objv[];              /* Argument objects. */
1955{
1956    int ret, off, new;
1957    const char *toKey;
1958    Tcl_HashEntry *hPtr;
1959    Container *svObj = (Container*)arg;
1960
1961    /*
1962     * Syntax:
1963     *          tsv::move array key to
1964     *          $object move to
1965     */
1966
1967    ret = Sv_GetContainer(interp, objc, objv, &svObj, &off, 0);
1968    if (ret != TCL_OK) {
1969        return TCL_ERROR;
1970    }
1971
1972    toKey = Tcl_GetString(objv[off]);
1973    hPtr = Tcl_CreateHashEntry(&svObj->arrayPtr->vars, toKey, &new);
1974
1975    if (!new) {
1976        Tcl_AppendResult(interp, "key \"", toKey, "\" exists", NULL);
1977        goto cmd_err;
1978    }
1979    if (svObj->entryPtr) {
1980        char *key = Tcl_GetHashKey(&svObj->arrayPtr->vars, svObj->entryPtr);
1981        if (svObj->arrayPtr->psPtr) {
1982            PsStore *psPtr = svObj->arrayPtr->psPtr;
1983            if ((*psPtr->psDelete)(psPtr->psHandle, key) == -1) {
1984                char *err = (*psPtr->psError)(psPtr->psHandle);
1985                Tcl_SetObjResult(interp, Tcl_NewStringObj(err, -1));
1986                return TCL_ERROR;
1987            }
1988        }
1989        Tcl_DeleteHashEntry(svObj->entryPtr);
1990    }
1991
1992    svObj->entryPtr = hPtr;
1993    Tcl_SetHashValue(hPtr, svObj);
1994
1995    return Sv_PutContainer(interp, svObj, SV_CHANGED);
1996
1997 cmd_err:
1998    return Sv_PutContainer(interp, svObj, SV_ERROR);
1999
2000}
2001
2002/*
2003 *----------------------------------------------------------------------
2004 *
2005 * SvLockObjCmd --
2006 *
2007 *    This procedure is invoked to process "tsv::lock" Tcl command.
2008 *    See the user documentation for details on what it does.
2009 *
2010 * Results:
2011 *    A standard Tcl result.
2012 *
2013 * Side effects:
2014 *    See the user documentation.
2015 *
2016 *----------------------------------------------------------------------
2017 */
2018
2019static int
2020SvLockObjCmd(dummy, interp, objc, objv)
2021    ClientData dummy;                   /* Not used. */
2022    Tcl_Interp *interp;                 /* Current interpreter. */
2023    int objc;                           /* Number of arguments. */
2024    Tcl_Obj *const objv[];              /* Argument objects. */
2025{
2026    int ret;
2027    Tcl_Obj *scriptObj;
2028    Bucket *bucketPtr;
2029    Array *arrayPtr = NULL;
2030
2031    /*
2032     * Syntax:
2033     *
2034     *     tsv::lock array arg ?arg ...?
2035     */
2036
2037    if (objc < 3) {
2038        Tcl_AppendResult(interp, "wrong # args: should be \"",
2039                         Tcl_GetString(objv[0]), "array arg ?arg...?\"", NULL);
2040        return TCL_ERROR;
2041    }
2042
2043    arrayPtr  = LockArray(interp, Tcl_GetString(objv[1]), FLAGS_CREATEARRAY);
2044    bucketPtr = arrayPtr->bucketPtr;
2045
2046    /*
2047     * Evaluate passed arguments as Tcl script. Note that
2048     * Tcl_EvalObjEx throws away the passed object by
2049     * doing an decrement reference count on it. This also
2050     * means we need not build object bytecode rep.
2051     */
2052
2053    if (objc == 3) {
2054        scriptObj = Tcl_DuplicateObj(objv[2]);
2055    } else {
2056        scriptObj = Tcl_ConcatObj(objc-2, objv + 2);
2057    }
2058
2059    Tcl_AllowExceptions(interp);
2060    ret = Tcl_EvalObjEx(interp, scriptObj, TCL_EVAL_DIRECT);
2061
2062    if (ret == TCL_ERROR) {
2063        char msg[32 + TCL_INTEGER_SPACE];
2064        sprintf(msg, "\n    (\"eval\" body line %d)", ERRORLINE(interp));
2065        Tcl_AddObjErrorInfo(interp, msg, -1);
2066    }
2067
2068    /*
2069     * We unlock the bucket directly, w/o going to Sv_Unlock()
2070     * since it needs the array which may be unset by the script.
2071     */
2072
2073    UNLOCK_BUCKET(bucketPtr);
2074
2075    return ret;
2076}
2077
2078/*
2079 *-----------------------------------------------------------------------------
2080 *
2081 * Sv_RegisterStdCommands --
2082 *
2083 *      Register standard shared variable commands
2084 *
2085 * Results:
2086 *      A standard Tcl result.
2087 *
2088 * Side effects:
2089 *      Memory gets allocated
2090 *
2091 *-----------------------------------------------------------------------------
2092 */
2093
2094static void
2095SvRegisterStdCommands(void)
2096{
2097    static int initialized = 0;
2098
2099    if (initialized == 0) {
2100        Tcl_MutexLock(&initMutex);
2101        if (initialized == 0) {
2102            Sv_RegisterCommand("var",    SvObjObjCmd,    NULL, NULL);
2103            Sv_RegisterCommand("object", SvObjObjCmd,    NULL, NULL);
2104            Sv_RegisterCommand("set",    SvSetObjCmd,    NULL, NULL);
2105            Sv_RegisterCommand("unset",  SvUnsetObjCmd,  NULL, NULL);
2106            Sv_RegisterCommand("get",    SvGetObjCmd,    NULL, NULL);
2107            Sv_RegisterCommand("incr",   SvIncrObjCmd,   NULL, NULL);
2108            Sv_RegisterCommand("exists", SvExistsObjCmd, NULL, NULL);
2109            Sv_RegisterCommand("append", SvAppendObjCmd, NULL, NULL);
2110            Sv_RegisterCommand("array",  SvArrayObjCmd,  NULL, NULL);
2111            Sv_RegisterCommand("names",  SvNamesObjCmd,  NULL, NULL);
2112            Sv_RegisterCommand("pop",    SvPopObjCmd,    NULL, NULL);
2113            Sv_RegisterCommand("move",   SvMoveObjCmd,   NULL, NULL);
2114            Sv_RegisterCommand("lock",   SvLockObjCmd,   NULL, NULL);
2115            initialized = 1;
2116        }
2117        Tcl_MutexUnlock(&initMutex);
2118    }
2119}
2120
2121/*
2122 *-----------------------------------------------------------------------------
2123 *
2124 * Sv_Init --
2125 *
2126 *    Creates commands in current interpreter.
2127 *
2128 * Results:
2129 *    None.
2130 *
2131 * Side effects
2132 *    Many new command created in current interpreter. Global data
2133 *    structures used by them initialized as well.
2134 *
2135 *-----------------------------------------------------------------------------
2136 */
2137int
2138Sv_Init (interp)
2139    Tcl_Interp *interp;
2140{
2141    register int i;
2142    Bucket *bucketPtr;
2143    SvCmdInfo *cmdPtr;
2144
2145    /*
2146     * Add keyed-list datatype
2147     */
2148
2149    TclX_KeyedListInit(interp);
2150    Sv_RegisterKeylistCommands();
2151
2152    /*
2153     * Register standard (nsv_* compatible) and our
2154     * own extensive set of list manipulating commands
2155     */
2156
2157    SvRegisterStdCommands();
2158    Sv_RegisterListCommands();
2159
2160    /*
2161     * Get Tcl object types. These are used
2162     * in custom object duplicator function.
2163     */
2164
2165    booleanObjTypePtr   = Tcl_GetObjType("boolean");
2166    byteArrayObjTypePtr = Tcl_GetObjType("bytearray");
2167    doubleObjTypePtr    = Tcl_GetObjType("double");
2168    intObjTypePtr       = Tcl_GetObjType("int");
2169    stringObjTypePtr    = Tcl_GetObjType("string");
2170
2171    /*
2172     * Plug-in registered commands in current interpreter
2173     */
2174
2175    for (cmdPtr = svCmdInfo; cmdPtr; cmdPtr = cmdPtr->nextPtr) {
2176        Tcl_CreateObjCommand(interp, cmdPtr->cmdName, cmdPtr->objProcPtr,
2177                (ClientData)cmdPtr->clientData, (Tcl_CmdDeleteProc*)0);
2178    }
2179
2180    /*
2181     * Create array of buckets and initialize each bucket
2182     */
2183
2184    if (buckets == NULL) {
2185        Tcl_MutexLock(&bucketsMutex);
2186        if (buckets == NULL) {
2187            buckets = (Bucket *)Tcl_Alloc(sizeof(Bucket) * NUMBUCKETS);
2188            for (i = 0; i < NUMBUCKETS; ++i) {
2189                bucketPtr = &buckets[i];
2190                memset(bucketPtr, 0, sizeof(Bucket));
2191                Tcl_InitHashTable(&bucketPtr->arrays, TCL_STRING_KEYS);
2192                Tcl_InitHashTable(&bucketPtr->handles, TCL_ONE_WORD_KEYS);
2193            }
2194
2195            /*
2196             * There is no other way to get Sv_tclEmptyStringRep
2197             * pointer value w/o this trick.
2198             */
2199
2200            {
2201                Tcl_Obj *dummy = Tcl_NewObj();
2202                Sv_tclEmptyStringRep = dummy->bytes;
2203                Tcl_DecrRefCount(dummy);
2204            }
2205
2206#ifdef HAVE_GDBM
2207            /*
2208             * Register persistent store handlers
2209             */
2210            Sv_RegisterGdbmStore();
2211#endif
2212        }
2213        Tcl_MutexUnlock(&bucketsMutex);
2214    }
2215
2216    return TCL_OK;
2217}
2218
2219int Sv_SafeInit (interp)
2220    Tcl_Interp *interp;
2221{
2222    return (Sv_Init(interp));
2223}
2224
2225
2226#ifdef SV_FINALIZE
2227/*
2228 * Left for reference, but unused since multithreaded finalization is
2229 * unsolvable in the general case. Brave souls can revive this by
2230 * installing a late exit handler on Thread's behalf, bringing the
2231 * function back onto the Tcl_Finalize (but not Tcl_Exit) path.
2232 */
2233
2234/*
2235 *-----------------------------------------------------------------------------
2236 *
2237 * SvFinalize --
2238 *
2239 *    Unset all arrays and reclaim all buckets.
2240 *
2241 * Results:
2242 *    None.
2243 *
2244 * Side effects
2245 *    Memory gets reclaimed.
2246 *
2247 *-----------------------------------------------------------------------------
2248 */
2249
2250static void
2251SvFinalize (clientData)
2252    ClientData clientData;
2253{
2254    register int i;
2255    SvCmdInfo *cmdPtr;
2256    RegType *regPtr;
2257
2258    Tcl_HashEntry *hashPtr;
2259    Tcl_HashSearch search;
2260
2261    /*
2262     * Reclaim memory for shared arrays
2263     */
2264
2265    if (buckets != NULL) {
2266        Tcl_MutexLock(&bucketsMutex);
2267        if (buckets != NULL) {
2268            for (i = 0; i < NUMBUCKETS; ++i) {
2269                Bucket *bucketPtr = &buckets[i];
2270                hashPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search);
2271                while (hashPtr != NULL) {
2272                    Array *arrayPtr = (Array*)Tcl_GetHashValue(hashPtr);
2273                    UnlockArray(arrayPtr);
2274                    DeleteArray(arrayPtr);
2275                    hashPtr = Tcl_NextHashEntry(&search);
2276                }
2277                if (bucketPtr->lock) {
2278                    Sp_RecursiveMutexFinalize(&bucketPtr->lock);
2279                }
2280                SvFinalizeContainers(bucketPtr);
2281                Tcl_DeleteHashTable(&bucketPtr->handles);
2282                Tcl_DeleteHashTable(&bucketPtr->arrays);
2283            }
2284            Tcl_Free((char *)buckets), buckets = NULL;
2285        }
2286        buckets = NULL;
2287        Tcl_MutexUnlock(&bucketsMutex);
2288    }
2289
2290    Tcl_MutexLock(&svMutex);
2291
2292    /*
2293     * Reclaim memory for registered commands
2294     */
2295
2296    if (svCmdInfo != NULL) {
2297        cmdPtr = svCmdInfo;
2298        while (cmdPtr) {
2299            SvCmdInfo *tmpPtr = cmdPtr->nextPtr;
2300            Tcl_Free((char*)cmdPtr);
2301            cmdPtr = tmpPtr;
2302        }
2303        svCmdInfo = NULL;
2304    }
2305
2306    /*
2307     * Reclaim memory for registered object types
2308     */
2309
2310    if (regType != NULL) {
2311        regPtr = regType;
2312        while (regPtr) {
2313            RegType *tmpPtr = regPtr->nextPtr;
2314            Tcl_Free((char*)regPtr);
2315            regPtr = tmpPtr;
2316        }
2317        regType = NULL;
2318    }
2319
2320    Tcl_MutexUnlock(&svMutex);
2321}
2322#endif /* SV_FINALIZE */
2323
2324/* EOF $RCSfile: threadSvCmd.c,v $ */
2325
2326/* Emacs Setup Variables */
2327/* Local Variables:      */
2328/* mode: C               */
2329/* indent-tabs-mode: nil */
2330/* c-basic-offset: 4     */
2331/* End:                  */
2332
2333