1 /*
2 * tclXkeylist.c --
3 *
4 * Extended Tcl keyed list commands and interfaces.
5 *-----------------------------------------------------------------------------
6 * Copyright 1991-1999 Karl Lehenbauer and Mark Diekhans.
7 *
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted, provided
10 * that the above copyright notice appear in all copies. Karl Lehenbauer and
11 * Mark Diekhans make no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
13 * implied warranty.
14 *-----------------------------------------------------------------------------
15 * $Id: tclXkeylist.c,v 1.8 2005/11/21 18:54:13 hobbs Exp $
16 *-----------------------------------------------------------------------------
17 */
18
19 #include "tclExtdInt.h"
20
21 /*
22 * Keyed lists are stored as arrays recursively defined objects. The data
23 * portion of a keyed list entry is a Tcl_Obj which may be a keyed list object
24 * or any other Tcl object. Since determine the structure of a keyed list is
25 * lazy (you don't know if an element is data or another keyed list) until it
26 * is accessed, the object can be transformed into a keyed list from a Tcl
27 * string or list.
28 */
29
30 /*
31 * Adding a hash table over the entries allows for much faster Find
32 * access to the keys (hash lookup instead of list search). This adds
33 * a hash table to each keyed list object. That uses more memory, but
34 * you can get an order of magnitude better performance with large
35 * keyed list sets. Uncomment this line to not use the hash table.
36 */
37 /* #define NO_KEYLIST_HASH_TABLE */
38
39 /*
40 * An entry in a keyed list array.
41 *
42 * JH: There was the supposition that making the key an object would
43 * be faster, but I tried that and didn't find it to be true. The
44 * use of the layered hash table is a big win though.
45 */
46 typedef struct {
47 char *key;
48 int keyLen;
49 Tcl_Obj *valuePtr;
50 } keylEntry_t;
51
52 /*
53 * Internal representation of a keyed list object.
54 */
55 typedef struct {
56 int arraySize; /* Current slots available in the array. */
57 int numEntries; /* Number of actual entries in the array. */
58 keylEntry_t *entries; /* Array of keyed list entries. */
59 #ifndef NO_KEYLIST_HASH_TABLE
60 Tcl_HashTable *hashTbl; /* hash table mirror of the entries */
61 /* to improve speed */
62 #endif
63 } keylIntObj_t;
64
65 /*
66 * Amount to increment array size by when it needs to grow.
67 */
68 #define KEYEDLIST_ARRAY_INCR_SIZE 16
69
70 /*
71 * Macro to duplicate a child entry of a keyed list if it is share by more
72 * than the parent.
73 * NO_KEYLIST_HASH_TABLE: We don't duplicate the hash table, so ensure
74 * that consistency checks allow for portions where not all entries are
75 * in the hash table.
76 */
77 #define DupSharedKeyListChild(keylIntPtr, idx) \
78 if (Tcl_IsShared(keylIntPtr->entries [idx].valuePtr)) { \
79 keylIntPtr->entries [idx].valuePtr = \
80 Tcl_DuplicateObj (keylIntPtr->entries [idx].valuePtr); \
81 Tcl_IncrRefCount(keylIntPtr->entries [idx].valuePtr); \
82 }
83
84 /*
85 * Macros to validate an keyed list object or internal representation
86 */
87 #ifdef TCLX_DEBUG
88 # define KEYL_OBJ_ASSERT(keylAPtr) {\
89 TclX_Assert (keylAPtr->typePtr == &keyedListType); \
90 ValidateKeyedList (keylAIntPtr); \
91 }
92 # define KEYL_REP_ASSERT(keylAIntPtr) \
93 ValidateKeyedList (keylAIntPtr)
94 #else
95 # define KEYL_REP_ASSERT(keylAIntPtr)
96 #endif
97
98
99 /*
100 * Prototypes of internal functions.
101 */
102 #ifdef TCLX_DEBUG
103 static void
104 ValidateKeyedList _ANSI_ARGS_((keylIntObj_t *keylIntPtr));
105 #endif
106 static int
107 ValidateKey _ANSI_ARGS_((Tcl_Interp *interp, char *key, int keyLen));
108
109 static keylIntObj_t *
110 AllocKeyedListIntRep _ANSI_ARGS_((void));
111
112 static void
113 FreeKeyedListData _ANSI_ARGS_((keylIntObj_t *keylIntPtr));
114
115 static void
116 EnsureKeyedListSpace _ANSI_ARGS_((keylIntObj_t *keylIntPtr,
117 int newNumEntries));
118
119 static void
120 DeleteKeyedListEntry _ANSI_ARGS_((keylIntObj_t *keylIntPtr,
121 int entryIdx));
122
123 static intptr_t
124 FindKeyedListEntry _ANSI_ARGS_((keylIntObj_t *keylIntPtr,
125 const char *key,
126 int *keyLenPtr,
127 const char **nextSubKeyPtr));
128
129 static void
130 DupKeyedListInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
131 Tcl_Obj *copyPtr));
132
133 static void
134 FreeKeyedListInternalRep _ANSI_ARGS_((Tcl_Obj *keylPtr));
135
136 static int
137 SetKeyedListFromAny _ANSI_ARGS_((Tcl_Interp *interp,
138 Tcl_Obj *objPtr));
139
140 static void
141 UpdateStringOfKeyedList _ANSI_ARGS_((Tcl_Obj *keylPtr));
142
143 static int
144 TclX_KeylgetObjCmd _ANSI_ARGS_((ClientData clientData,
145 Tcl_Interp *interp,
146 int objc,
147 Tcl_Obj *CONST objv[]));
148
149 static int
150 TclX_KeylsetObjCmd _ANSI_ARGS_((ClientData clientData,
151 Tcl_Interp *interp,
152 int objc,
153 Tcl_Obj *CONST objv[]));
154
155 static int
156 TclX_KeyldelObjCmd _ANSI_ARGS_((ClientData clientData,
157 Tcl_Interp *interp,
158 int objc,
159 Tcl_Obj *CONST objv[]));
160
161 static int
162 TclX_KeylkeysObjCmd _ANSI_ARGS_((ClientData clientData,
163 Tcl_Interp *interp,
164 int objc,
165 Tcl_Obj *CONST objv[]));
166
167 /*
168 * Type definition.
169 */
170 static Tcl_ObjType keyedListType = {
171 "keyedList", /* name */
172 FreeKeyedListInternalRep, /* freeIntRepProc */
173 DupKeyedListInternalRep, /* dupIntRepProc */
174 UpdateStringOfKeyedList, /* updateStringProc */
175 SetKeyedListFromAny /* setFromAnyProc */
176 };
177
178
179 /*-----------------------------------------------------------------------------
180 * ValidateKeyedList --
181 * Validate a keyed list (only when TCLX_DEBUG is enabled).
182 * Parameters:
183 * o keylIntPtr - Keyed list internal representation.
184 *-----------------------------------------------------------------------------
185 */
186 #ifdef TCLX_DEBUG
187 static void
ValidateKeyedList(keylIntPtr)188 ValidateKeyedList (keylIntPtr)
189 keylIntObj_t *keylIntPtr;
190 {
191 int idx;
192
193 TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
194 TclX_Assert (keylIntPtr->arraySize >= 0);
195 TclX_Assert (keylIntPtr->numEntries >= 0);
196 TclX_Assert ((keylIntPtr->arraySize > 0) ?
197 (keylIntPtr->entries != NULL) : TRUE);
198 TclX_Assert ((keylIntPtr->numEntries > 0) ?
199 (keylIntPtr->entries != NULL) : TRUE);
200
201 for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
202 keylEntry_t *entryPtr = &(keylIntPtr->entries [idx]);
203 TclX_Assert (entryPtr->key != NULL);
204 TclX_Assert (entryPtr->valuePtr->refCount >= 1);
205 if (entryPtr->valuePtr->typePtr == &keyedListType) {
206 ValidateKeyedList (entryPtr->valuePtr->internalRep.otherValuePtr);
207 }
208 }
209 }
210 #endif
211
212 /*-----------------------------------------------------------------------------
213 * ValidateKey --
214 * Check that a key or keypath string is a valid value.
215 *
216 * Parameters:
217 * o interp - Used to return error messages.
218 * o key - Key string to check.
219 * o keyLen - Length of the string, used to check for binary data.
220 * Returns:
221 * TCL_OK or TCL_ERROR.
222 *-----------------------------------------------------------------------------
223 */
224 static int
ValidateKey(interp,key,keyLen)225 ValidateKey (interp, key, keyLen)
226 Tcl_Interp *interp;
227 char *key;
228 int keyLen;
229 {
230 if (strlen (key) != (size_t) keyLen) {
231 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
232 "keyed list key may not be a binary string", (char *) NULL);
233 return TCL_ERROR;
234 }
235 if (keyLen == 0) {
236 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
237 "keyed list key may not be an empty string", (char *) NULL);
238 return TCL_ERROR;
239 }
240 return TCL_OK;
241 }
242
243
244 /*-----------------------------------------------------------------------------
245 * AllocKeyedListIntRep --
246 * Allocate an and initialize the keyed list internal representation.
247 *
248 * Returns:
249 * A pointer to the keyed list internal structure.
250 *-----------------------------------------------------------------------------
251 */
252 static keylIntObj_t *
AllocKeyedListIntRep()253 AllocKeyedListIntRep ()
254 {
255 keylIntObj_t *keylIntPtr;
256
257 keylIntPtr = (keylIntObj_t *) ckalloc (sizeof (keylIntObj_t));
258 memset(keylIntPtr, 0, sizeof (keylIntObj_t));
259 #ifndef NO_KEYLIST_HASH_TABLE
260 keylIntPtr->hashTbl = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
261 Tcl_InitHashTable(keylIntPtr->hashTbl, TCL_STRING_KEYS);
262 #endif
263 return keylIntPtr;
264 }
265
266 /*-----------------------------------------------------------------------------
267 * FreeKeyedListData --
268 * Free the internal representation of a keyed list.
269 *
270 * Parameters:
271 * o keylIntPtr - Keyed list internal structure to free.
272 *-----------------------------------------------------------------------------
273 */
274 static void
FreeKeyedListData(keylIntPtr)275 FreeKeyedListData (keylIntPtr)
276 keylIntObj_t *keylIntPtr;
277 {
278 int idx;
279
280 for (idx = 0; idx < keylIntPtr->numEntries ; idx++) {
281 ckfree (keylIntPtr->entries [idx].key);
282 Tcl_DecrRefCount(keylIntPtr->entries [idx].valuePtr);
283 }
284 if (keylIntPtr->entries != NULL)
285 ckfree ((VOID*) keylIntPtr->entries);
286 #ifndef NO_KEYLIST_HASH_TABLE
287 if (keylIntPtr->hashTbl != NULL) {
288 Tcl_DeleteHashTable(keylIntPtr->hashTbl);
289 ckfree((char *) (keylIntPtr->hashTbl));
290 }
291 #endif
292 ckfree ((VOID*) keylIntPtr);
293 }
294
295 /*-----------------------------------------------------------------------------
296 * EnsureKeyedListSpace --
297 * Ensure there is enough room in a keyed list array for a certain number
298 * of entries, expanding if necessary.
299 *
300 * Parameters:
301 * o keylIntPtr - Keyed list internal representation.
302 * o newNumEntries - The number of entries that are going to be added to
303 * the keyed list.
304 *-----------------------------------------------------------------------------
305 */
306 static void
EnsureKeyedListSpace(keylIntPtr,newNumEntries)307 EnsureKeyedListSpace (keylIntPtr, newNumEntries)
308 keylIntObj_t *keylIntPtr;
309 int newNumEntries;
310 {
311 KEYL_REP_ASSERT (keylIntPtr);
312
313 if ((keylIntPtr->arraySize - keylIntPtr->numEntries) < newNumEntries) {
314 int newSize = keylIntPtr->arraySize + newNumEntries +
315 KEYEDLIST_ARRAY_INCR_SIZE;
316 if (keylIntPtr->entries == NULL) {
317 keylIntPtr->entries = (keylEntry_t *)
318 ckalloc (newSize * sizeof (keylEntry_t));
319 } else {
320 keylIntPtr->entries = (keylEntry_t *)
321 ckrealloc ((VOID *) keylIntPtr->entries,
322 newSize * sizeof (keylEntry_t));
323 }
324 keylIntPtr->arraySize = newSize;
325 }
326
327 KEYL_REP_ASSERT (keylIntPtr);
328 }
329
330 /*-----------------------------------------------------------------------------
331 * DeleteKeyedListEntry --
332 * Delete an entry from a keyed list.
333 *
334 * Parameters:
335 * o keylIntPtr - Keyed list internal representation.
336 * o entryIdx - Index of entry to delete.
337 *-----------------------------------------------------------------------------
338 */
339 static void
DeleteKeyedListEntry(keylIntPtr,entryIdx)340 DeleteKeyedListEntry (keylIntPtr, entryIdx)
341 keylIntObj_t *keylIntPtr;
342 int entryIdx;
343 {
344 intptr_t idx;
345
346 #ifndef NO_KEYLIST_HASH_TABLE
347 if (keylIntPtr->hashTbl != NULL) {
348 Tcl_HashEntry *entryPtr;
349 Tcl_HashSearch search;
350 intptr_t nidx;
351
352 entryPtr = Tcl_FindHashEntry(keylIntPtr->hashTbl,
353 keylIntPtr->entries [entryIdx].key);
354 if (entryPtr != NULL) {
355 Tcl_DeleteHashEntry(entryPtr);
356 }
357
358 /*
359 * In order to maintain consistency, we have to iterate over
360 * the entire hash table to find and decr relevant idxs.
361 * We have to do this even if the previous index was not found
362 * in the hash table, as Dup'ing doesn't dup the hash tables.
363 */
364 for (entryPtr = Tcl_FirstHashEntry(keylIntPtr->hashTbl, &search);
365 entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
366 nidx = (intptr_t) Tcl_GetHashValue(entryPtr);
367 if (nidx > entryIdx) {
368 Tcl_SetHashValue(entryPtr, (ClientData) (nidx - 1));
369 }
370 }
371 }
372 #endif
373
374 ckfree (keylIntPtr->entries [entryIdx].key);
375 Tcl_DecrRefCount(keylIntPtr->entries [entryIdx].valuePtr);
376
377 for (idx = entryIdx; idx < keylIntPtr->numEntries - 1; idx++)
378 keylIntPtr->entries [idx] = keylIntPtr->entries [idx + 1];
379 keylIntPtr->numEntries--;
380
381 KEYL_REP_ASSERT (keylIntPtr);
382 }
383
384 /*-----------------------------------------------------------------------------
385 * FindKeyedListEntry --
386 * Find an entry in keyed list.
387 *
388 * Parameters:
389 * o keylIntPtr - Keyed list internal representation.
390 * o key - Name of key to search for.
391 * o keyLenPtr - In not NULL, the length of the key for this
392 * level is returned here. This excludes subkeys and the `.' delimiters.
393 * o nextSubKeyPtr - If not NULL, the start of the name of the next
394 * sub-key within key is returned.
395 * Returns:
396 * Index of the entry or -1 if not found.
397 *-----------------------------------------------------------------------------
398 */
399 static intptr_t
FindKeyedListEntry(keylIntPtr,key,keyLenPtr,nextSubKeyPtr)400 FindKeyedListEntry (keylIntPtr, key, keyLenPtr, nextSubKeyPtr)
401 keylIntObj_t *keylIntPtr;
402 const char *key;
403 int *keyLenPtr;
404 const char **nextSubKeyPtr;
405 {
406 const char *keySeparPtr;
407 int keyLen;
408 intptr_t findIdx = -1;
409
410 keySeparPtr = strchr (key, '.');
411 if (keySeparPtr != NULL) {
412 keyLen = keySeparPtr - key;
413 } else {
414 keyLen = strlen (key);
415 }
416
417 #ifndef NO_KEYLIST_HASH_TABLE
418 if (keylIntPtr->hashTbl != NULL) {
419 Tcl_HashEntry *entryPtr;
420 char *tmp;
421 const char *_key;
422
423 if (keySeparPtr != NULL) {
424 tmp = alloca(keyLen + 1);
425 strncpy(tmp, key, keyLen);
426 tmp[keyLen] = '\0';
427 _key = tmp;
428 } else
429 _key = key;
430 entryPtr = Tcl_FindHashEntry(keylIntPtr->hashTbl, _key);
431 if (entryPtr != NULL) {
432 findIdx = (intptr_t)Tcl_GetHashValue(entryPtr);
433 }
434 }
435 #endif
436
437 if (findIdx == -1) {
438 for (findIdx = 0; findIdx < keylIntPtr->numEntries; findIdx++) {
439 if (keylIntPtr->entries [findIdx].keyLen == keyLen
440 && STRNEQU(keylIntPtr->entries [findIdx].key, key, keyLen)) {
441 break;
442 }
443 }
444 }
445
446 if (nextSubKeyPtr != NULL) {
447 if (keySeparPtr == NULL) {
448 *nextSubKeyPtr = NULL;
449 } else {
450 *nextSubKeyPtr = keySeparPtr + 1;
451 }
452 }
453 if (keyLenPtr != NULL) {
454 *keyLenPtr = keyLen;
455 }
456
457 if (findIdx >= keylIntPtr->numEntries) {
458 return -1;
459 }
460
461 return findIdx;
462 }
463
464 /*-----------------------------------------------------------------------------
465 * FreeKeyedListInternalRep --
466 * Free the internal representation of a keyed list.
467 *
468 * Parameters:
469 * o keylPtr - Keyed list object being deleted.
470 *-----------------------------------------------------------------------------
471 */
472 static void
FreeKeyedListInternalRep(keylPtr)473 FreeKeyedListInternalRep (keylPtr)
474 Tcl_Obj *keylPtr;
475 {
476 FreeKeyedListData ((keylIntObj_t *) keylPtr->internalRep.otherValuePtr);
477 }
478
479 /*-----------------------------------------------------------------------------
480 * DupKeyedListInternalRep --
481 * Duplicate the internal representation of a keyed list.
482 *
483 * Parameters:
484 * o srcPtr - Keyed list object to copy.
485 * o copyPtr - Target object to copy internal representation to.
486 *-----------------------------------------------------------------------------
487 */
488 static void
DupKeyedListInternalRep(srcPtr,copyPtr)489 DupKeyedListInternalRep (srcPtr, copyPtr)
490 Tcl_Obj *srcPtr;
491 Tcl_Obj *copyPtr;
492 {
493 keylIntObj_t *srcIntPtr =
494 (keylIntObj_t *) srcPtr->internalRep.otherValuePtr;
495 keylIntObj_t *copyIntPtr;
496 int idx;
497
498 KEYL_REP_ASSERT (srcIntPtr);
499
500 copyIntPtr = (keylIntObj_t *) ckalloc (sizeof (keylIntObj_t));
501 copyIntPtr->arraySize = srcIntPtr->arraySize;
502 copyIntPtr->numEntries = srcIntPtr->numEntries;
503 copyIntPtr->entries = (keylEntry_t *)
504 ckalloc (copyIntPtr->arraySize * sizeof (keylEntry_t));
505 #ifndef NO_KEYLIST_HASH_TABLE
506 #if 0
507 copyIntPtr->hashTbl = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
508 Tcl_InitHashTable(copyIntPtr->hashTbl, TCL_STRING_KEYS);
509 #else
510 /*
511 * NO_KEYLIST_HASH_TABLE: We don't duplicate the hash table, so ensure
512 * that consistency checks allow for portions where not all entries are
513 * in the hash table.
514 */
515 copyIntPtr->hashTbl = NULL;
516 #endif
517 #endif
518
519 for (idx = 0; idx < srcIntPtr->numEntries ; idx++) {
520 copyIntPtr->entries [idx].key =
521 ckstrdup (srcIntPtr->entries [idx].key);
522 copyIntPtr->entries [idx].keyLen = srcIntPtr->entries [idx].keyLen;
523 copyIntPtr->entries [idx].valuePtr =
524 Tcl_DuplicateObj(srcIntPtr->entries [idx].valuePtr);
525 Tcl_IncrRefCount(copyIntPtr->entries [idx].valuePtr);
526 #ifndef NO_KEYLIST_HASH_TABLE
527 /*
528 * If we dup the hash table as well and do other better tracking
529 * of all access, then we could remove the entries list.
530 */
531 #endif
532 }
533
534 copyPtr->internalRep.otherValuePtr = (VOID *) copyIntPtr;
535 copyPtr->typePtr = &keyedListType;
536
537 KEYL_REP_ASSERT (copyIntPtr);
538 }
539
540 /*-----------------------------------------------------------------------------
541 * SetKeyedListFromAny --
542 * Convert an object to a keyed list from its string representation. Only
543 * the first level is converted, as there is no way of knowing how far down
544 * the keyed list recurses until lower levels are accessed.
545 *
546 * Parameters:
547 * o objPtr - Object to convert to a keyed list.
548 *-----------------------------------------------------------------------------
549 */
550 static int
SetKeyedListFromAny(interp,objPtr)551 SetKeyedListFromAny (interp, objPtr)
552 Tcl_Interp *interp;
553 Tcl_Obj *objPtr;
554 {
555 keylIntObj_t *keylIntPtr;
556 keylEntry_t *keyEntryPtr;
557 char *key;
558 int keyLen, objc, subObjc;
559 intptr_t idx;
560 Tcl_Obj **objv, **subObjv;
561 #ifndef NO_KEYLIST_HASH_TABLE
562 int dummy;
563 Tcl_HashEntry *entryPtr;
564 #endif
565
566 if (Tcl_ListObjGetElements (interp, objPtr, &objc, &objv) != TCL_OK) {
567 return TCL_ERROR;
568 }
569
570 keylIntPtr = AllocKeyedListIntRep();
571
572 EnsureKeyedListSpace(keylIntPtr, objc);
573
574 for (idx = 0; idx < objc; idx++) {
575 if ((Tcl_ListObjGetElements(interp, objv[idx],
576 &subObjc, &subObjv) != TCL_OK)
577 || (subObjc != 2)) {
578 Tcl_ResetResult(interp);
579 Tcl_AppendStringsToObj(Tcl_GetObjResult (interp),
580 "keyed list entry must be a valid, 2 element list, got \"",
581 Tcl_GetString(objv[idx]), "\"", (char *) NULL);
582 FreeKeyedListData(keylIntPtr);
583 return TCL_ERROR;
584 }
585
586 key = Tcl_GetStringFromObj(subObjv[0], &keyLen);
587 if (ValidateKey(interp, key, keyLen) == TCL_ERROR) {
588 FreeKeyedListData (keylIntPtr);
589 return TCL_ERROR;
590 }
591 /*
592 * When setting from a random list/string, we cannot allow
593 * keys to have embedded '.' path separators
594 */
595 if ((strchr(key, '.') != NULL)) {
596 Tcl_AppendStringsToObj (Tcl_GetObjResult (interp),
597 "keyed list key may not contain a \".\"; ",
598 "it is used as a separator in key paths",
599 (char *) NULL);
600 FreeKeyedListData (keylIntPtr);
601 return TCL_ERROR;
602 }
603 keyEntryPtr = &(keylIntPtr->entries[idx]);
604
605 keyEntryPtr->key = ckstrdup(key);
606 keyEntryPtr->keyLen = keyLen;
607 keyEntryPtr->valuePtr = Tcl_DuplicateObj(subObjv[1]);
608 Tcl_IncrRefCount(keyEntryPtr->valuePtr);
609 #ifndef NO_KEYLIST_HASH_TABLE
610 entryPtr = Tcl_CreateHashEntry(keylIntPtr->hashTbl,
611 keyEntryPtr->key, &dummy);
612 Tcl_SetHashValue(entryPtr, (ClientData) idx);
613 #endif
614
615 keylIntPtr->numEntries++;
616 }
617
618 if ((objPtr->typePtr != NULL) &&
619 (objPtr->typePtr->freeIntRepProc != NULL)) {
620 (*objPtr->typePtr->freeIntRepProc) (objPtr);
621 }
622 objPtr->internalRep.otherValuePtr = (VOID *) keylIntPtr;
623 objPtr->typePtr = &keyedListType;
624
625 KEYL_REP_ASSERT (keylIntPtr);
626 return TCL_OK;
627 }
628
629 /*-----------------------------------------------------------------------------
630 * UpdateStringOfKeyedList --
631 * Update the string representation of a keyed list.
632 *
633 * Parameters:
634 * o objPtr - Object to convert to a keyed list.
635 *-----------------------------------------------------------------------------
636 */
637 static void
UpdateStringOfKeyedList(keylPtr)638 UpdateStringOfKeyedList (keylPtr)
639 Tcl_Obj *keylPtr;
640 {
641 #define UPDATE_STATIC_SIZE 32
642 int idx, strLen;
643 Tcl_Obj **listObjv, *entryObjv [2], *tmpListObj;
644 Tcl_Obj *staticListObjv [UPDATE_STATIC_SIZE];
645 char *listStr;
646 keylIntObj_t *keylIntPtr =
647 (keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
648
649 /*
650 * Conversion to strings is done via list objects to support binary data.
651 */
652 if (keylIntPtr->numEntries > UPDATE_STATIC_SIZE) {
653 listObjv =
654 (Tcl_Obj **) ckalloc (keylIntPtr->numEntries * sizeof (Tcl_Obj *));
655 } else {
656 listObjv = staticListObjv;
657 }
658
659 /*
660 * Convert each keyed list entry to a two element list object. No
661 * need to incr/decr ref counts, the list objects will take care of that.
662 * FIX: Keeping key as string object will speed this up.
663 */
664 for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
665 entryObjv [0] =
666 Tcl_NewStringObj (keylIntPtr->entries [idx].key,
667 keylIntPtr->entries [idx].keyLen);
668 entryObjv [1] = keylIntPtr->entries [idx].valuePtr;
669 listObjv [idx] = Tcl_NewListObj (2, entryObjv);
670 }
671
672 tmpListObj = Tcl_NewListObj (keylIntPtr->numEntries, listObjv);
673 Tcl_IncrRefCount(tmpListObj);
674 listStr = Tcl_GetStringFromObj (tmpListObj, &strLen);
675 keylPtr->bytes = ckbinstrdup (listStr, strLen);
676 keylPtr->length = strLen;
677 Tcl_DecrRefCount(tmpListObj);
678
679 if (listObjv != staticListObjv)
680 ckfree ((VOID*) listObjv);
681 }
682
683 /*-----------------------------------------------------------------------------
684 * TclX_NewKeyedListObj --
685 * Create and initialize a new keyed list object.
686 *
687 * Returns:
688 * A pointer to the object.
689 *-----------------------------------------------------------------------------
690 */
691 Tcl_Obj *
TclX_NewKeyedListObj()692 TclX_NewKeyedListObj ()
693 {
694 Tcl_Obj *keylPtr = Tcl_NewObj ();
695 keylIntObj_t *keylIntPtr = AllocKeyedListIntRep ();
696
697 keylPtr->internalRep.otherValuePtr = (VOID *) keylIntPtr;
698 keylPtr->typePtr = &keyedListType;
699 return keylPtr;
700 }
701
702 /*-----------------------------------------------------------------------------
703 * TclX_KeyedListGet --
704 * Retrieve a key value from a keyed list.
705 *
706 * Parameters:
707 * o interp - Error message will be return in result if there is an error.
708 * o keylPtr - Keyed list object to get key from.
709 * o key - The name of the key to extract. Will recusively process sub-keys
710 * seperated by `.'.
711 * o valueObjPtrPtr - If the key is found, a pointer to the key object
712 * is returned here. NULL is returned if the key is not present.
713 * Returns:
714 * o TCL_OK - If the key value was returned.
715 * o TCL_BREAK - If the key was not found.
716 * o TCL_ERROR - If an error occured.
717 *-----------------------------------------------------------------------------
718 */
719 int
TclX_KeyedListGet(interp,keylPtr,key,valuePtrPtr)720 TclX_KeyedListGet (interp, keylPtr, key, valuePtrPtr)
721 Tcl_Interp *interp;
722 Tcl_Obj *keylPtr;
723 const char *key;
724 Tcl_Obj **valuePtrPtr;
725 {
726 keylIntObj_t *keylIntPtr;
727 const char *nextSubKey;
728 int findIdx;
729
730 while (1) {
731 if (Tcl_ConvertToType (interp, keylPtr, &keyedListType) != TCL_OK)
732 return TCL_ERROR;
733 keylIntPtr = (keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
734 KEYL_REP_ASSERT (keylIntPtr);
735
736 findIdx = FindKeyedListEntry(keylIntPtr, key, NULL, &nextSubKey);
737
738 /*
739 * If not found, return status.
740 */
741 if (findIdx < 0) {
742 *valuePtrPtr = NULL;
743 return TCL_BREAK;
744 }
745
746 /*
747 * If we are at the last subkey, return the entry, otherwise recurse
748 * down looking for the entry.
749 */
750 if (nextSubKey == NULL) {
751 *valuePtrPtr = keylIntPtr->entries [findIdx].valuePtr;
752 return TCL_OK;
753 } else {
754 keylPtr = keylIntPtr->entries [findIdx].valuePtr;
755 key = nextSubKey;
756 }
757 }
758 }
759
760 /*-----------------------------------------------------------------------------
761 * TclX_KeyedListSet --
762 * Set a key value in keyed list object.
763 *
764 * Parameters:
765 * o interp - Error message will be return in result object.
766 * o keylPtr - Keyed list object to update.
767 * o key - The name of the key to extract. Will recursively process
768 * sub-key seperated by `.'.
769 * o valueObjPtr - The value to set for the key.
770 * Returns:
771 * TCL_OK or TCL_ERROR.
772 *-----------------------------------------------------------------------------
773 */
774 int
TclX_KeyedListSet(interp,keylPtr,key,valuePtr)775 TclX_KeyedListSet (interp, keylPtr, key, valuePtr)
776 Tcl_Interp *interp;
777 Tcl_Obj *keylPtr;
778 const char *key;
779 Tcl_Obj *valuePtr;
780 {
781 keylIntObj_t *keylIntPtr;
782 keylEntry_t *keyEntryPtr;
783 const char *nextSubKey;
784 intptr_t findIdx;
785 int keyLen, status = TCL_OK;
786 Tcl_Obj *newKeylPtr;
787
788 while (1) {
789 if (Tcl_ConvertToType (interp, keylPtr, &keyedListType) != TCL_OK)
790 return TCL_ERROR;
791 keylIntPtr = (keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
792 KEYL_REP_ASSERT (keylIntPtr);
793
794 findIdx = FindKeyedListEntry (keylIntPtr, key, &keyLen, &nextSubKey);
795
796 /*
797 * If we are at the last subkey, either update or add an entry.
798 */
799 if (nextSubKey == NULL) {
800 #ifndef NO_KEYLIST_HASH_TABLE
801 int dummy;
802 Tcl_HashEntry *entryPtr;
803 #endif
804 if (findIdx < 0) {
805 EnsureKeyedListSpace (keylIntPtr, 1);
806 findIdx = keylIntPtr->numEntries++;
807 } else {
808 ckfree (keylIntPtr->entries [findIdx].key);
809 Tcl_DecrRefCount(keylIntPtr->entries [findIdx].valuePtr);
810 }
811 keyEntryPtr = &(keylIntPtr->entries[findIdx]);
812 keyEntryPtr->key = (char *) ckalloc (keyLen + 1);
813 memcpy(keyEntryPtr->key, key, keyLen);
814 keyEntryPtr->key[keyLen] = '\0';
815 keyEntryPtr->keyLen = keyLen;
816 keyEntryPtr->valuePtr = valuePtr;
817 Tcl_IncrRefCount(valuePtr);
818 #ifndef NO_KEYLIST_HASH_TABLE
819 if (keylIntPtr->hashTbl == NULL) {
820 keylIntPtr->hashTbl =
821 (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
822 Tcl_InitHashTable(keylIntPtr->hashTbl, TCL_STRING_KEYS);
823 }
824 entryPtr = Tcl_CreateHashEntry(keylIntPtr->hashTbl,
825 keyEntryPtr->key, &dummy);
826 Tcl_SetHashValue(entryPtr, (ClientData) findIdx);
827 #endif
828 Tcl_InvalidateStringRep (keylPtr);
829
830 KEYL_REP_ASSERT (keylIntPtr);
831 return TCL_OK;
832 }
833
834 /*
835 * If we are not at the last subkey, recurse down, creating new
836 * entries if neccessary. If this level key was not found, it
837 * means we must build new subtree. Don't insert the new tree until we
838 * come back without error.
839 */
840 if (findIdx >= 0) {
841 DupSharedKeyListChild (keylIntPtr, findIdx);
842 status = TclX_KeyedListSet (interp,
843 keylIntPtr->entries [findIdx].valuePtr,
844 nextSubKey, valuePtr);
845 if (status == TCL_OK) {
846 Tcl_InvalidateStringRep (keylPtr);
847 }
848 } else {
849 #ifndef NO_KEYLIST_HASH_TABLE
850 int dummy;
851 Tcl_HashEntry *entryPtr;
852 #endif
853 newKeylPtr = TclX_NewKeyedListObj ();
854 Tcl_IncrRefCount(newKeylPtr);
855 if (TclX_KeyedListSet (interp, newKeylPtr,
856 nextSubKey, valuePtr) != TCL_OK) {
857 Tcl_DecrRefCount(newKeylPtr);
858 return TCL_ERROR;
859 }
860 EnsureKeyedListSpace (keylIntPtr, 1);
861 findIdx = keylIntPtr->numEntries++;
862 keyEntryPtr = &(keylIntPtr->entries[findIdx]);
863 keyEntryPtr->key = (char *) ckalloc (keyLen + 1);
864 memcpy(keyEntryPtr->key, key, keyLen);
865 keyEntryPtr->key[keyLen] = '\0';
866 keyEntryPtr->keyLen = keyLen;
867 keyEntryPtr->valuePtr = newKeylPtr;
868 #ifndef NO_KEYLIST_HASH_TABLE
869 if (keylIntPtr->hashTbl == NULL) {
870 keylIntPtr->hashTbl =
871 (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
872 Tcl_InitHashTable(keylIntPtr->hashTbl, TCL_STRING_KEYS);
873 }
874 entryPtr = Tcl_CreateHashEntry(keylIntPtr->hashTbl,
875 keyEntryPtr->key, &dummy);
876 Tcl_SetHashValue(entryPtr, (ClientData) findIdx);
877 #endif
878 Tcl_InvalidateStringRep (keylPtr);
879 }
880
881 KEYL_REP_ASSERT (keylIntPtr);
882 return status;
883 }
884 }
885
886 /*-----------------------------------------------------------------------------
887 * TclX_KeyedListDelete --
888 * Delete a key value from keyed list.
889 *
890 * Parameters:
891 * o interp - Error message will be return in result if there is an error.
892 * o keylPtr - Keyed list object to update.
893 * o key - The name of the key to extract. Will recusively process
894 * sub-key seperated by `.'.
895 * Returns:
896 * o TCL_OK - If the key was deleted.
897 * o TCL_BREAK - If the key was not found.
898 * o TCL_ERROR - If an error occured.
899 *-----------------------------------------------------------------------------
900 */
901 int
TclX_KeyedListDelete(interp,keylPtr,key)902 TclX_KeyedListDelete (interp, keylPtr, key)
903 Tcl_Interp *interp;
904 Tcl_Obj *keylPtr;
905 const char *key;
906 {
907 keylIntObj_t *keylIntPtr, *subKeylIntPtr;
908 const char *nextSubKey;
909 intptr_t findIdx;
910 int status;
911
912 if (Tcl_ConvertToType (interp, keylPtr, &keyedListType) != TCL_OK)
913 return TCL_ERROR;
914 keylIntPtr = (keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
915
916 findIdx = FindKeyedListEntry (keylIntPtr, key, NULL, &nextSubKey);
917
918 /*
919 * If not found, return status.
920 */
921 if (findIdx < 0) {
922 KEYL_REP_ASSERT (keylIntPtr);
923 return TCL_BREAK;
924 }
925
926 /*
927 * If we are at the last subkey, delete the entry.
928 */
929 if (nextSubKey == NULL) {
930 DeleteKeyedListEntry (keylIntPtr, findIdx);
931 Tcl_InvalidateStringRep (keylPtr);
932
933 KEYL_REP_ASSERT (keylIntPtr);
934 return TCL_OK;
935 }
936
937 /*
938 * If we are not at the last subkey, recurse down. If the entry is
939 * deleted and the sub-keyed list is empty, delete it as well. Must
940 * invalidate string, as it caches all representations below it.
941 */
942 DupSharedKeyListChild (keylIntPtr, findIdx);
943
944 status = TclX_KeyedListDelete (interp,
945 keylIntPtr->entries [findIdx].valuePtr,
946 nextSubKey);
947 if (status == TCL_OK) {
948 subKeylIntPtr = (keylIntObj_t *)
949 keylIntPtr->entries [findIdx].valuePtr->internalRep.otherValuePtr;
950 if (subKeylIntPtr->numEntries == 0) {
951 DeleteKeyedListEntry (keylIntPtr, findIdx);
952 }
953 Tcl_InvalidateStringRep (keylPtr);
954 }
955
956 KEYL_REP_ASSERT (keylIntPtr);
957 return status;
958 }
959
960 /*-----------------------------------------------------------------------------
961 * TclX_KeyedListGetKeys --
962 * Retrieve a list of keyed list keys.
963 *
964 * Parameters:
965 * o interp - Error message will be return in result if there is an error.
966 * o keylPtr - Keyed list object to get key from.
967 * o key - The name of the key to get the sub keys for. NULL or empty
968 * to retrieve all top level keys.
969 * o listObjPtrPtr - List object is returned here with key as values.
970 * Returns:
971 * o TCL_OK - If the zero or more key where returned.
972 * o TCL_BREAK - If the key was not found.
973 * o TCL_ERROR - If an error occured.
974 *-----------------------------------------------------------------------------
975 */
976 int
TclX_KeyedListGetKeys(interp,keylPtr,key,listObjPtrPtr)977 TclX_KeyedListGetKeys (interp, keylPtr, key, listObjPtrPtr)
978 Tcl_Interp *interp;
979 Tcl_Obj *keylPtr;
980 const char *key;
981 Tcl_Obj **listObjPtrPtr;
982 {
983 keylIntObj_t *keylIntPtr;
984 Tcl_Obj *listObjPtr;
985 const char *nextSubKey;
986 int idx;
987 intptr_t findIdx;
988
989 if (Tcl_ConvertToType (interp, keylPtr, &keyedListType) != TCL_OK)
990 return TCL_ERROR;
991 keylIntPtr = (keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
992
993 /*
994 * If key is not NULL or empty, then recurse down until we go past
995 * the end of all of the elements of the key.
996 */
997 if ((key != NULL) && (key [0] != '\0')) {
998 findIdx = FindKeyedListEntry (keylIntPtr, key, NULL, &nextSubKey);
999 if (findIdx < 0) {
1000 TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
1001 return TCL_BREAK;
1002 }
1003 TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
1004 return TclX_KeyedListGetKeys (interp,
1005 keylIntPtr->entries [findIdx].valuePtr,
1006 nextSubKey,
1007 listObjPtrPtr);
1008 }
1009
1010 /*
1011 * Reached the end of the full key, return all keys at this level.
1012 */
1013 listObjPtr = Tcl_NewObj();
1014 for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
1015 Tcl_ListObjAppendElement(interp, listObjPtr,
1016 Tcl_NewStringObj(keylIntPtr->entries[idx].key,
1017 keylIntPtr->entries[idx].keyLen));
1018 }
1019 *listObjPtrPtr = listObjPtr;
1020 TclX_Assert (keylIntPtr->arraySize >= keylIntPtr->numEntries);
1021 return TCL_OK;
1022 }
1023
1024 /*-----------------------------------------------------------------------------
1025 * Tcl_KeylgetObjCmd --
1026 * Implements the TCL keylget command:
1027 * keylget listvar ?key? ?retvar | {}?
1028 *-----------------------------------------------------------------------------
1029 */
1030 static int
TclX_KeylgetObjCmd(clientData,interp,objc,objv)1031 TclX_KeylgetObjCmd (clientData, interp, objc, objv)
1032 ClientData clientData;
1033 Tcl_Interp *interp;
1034 int objc;
1035 Tcl_Obj *CONST objv[];
1036 {
1037 Tcl_Obj *keylPtr, *valuePtr;
1038 char *key;
1039 int keyLen, status;
1040
1041 if ((objc < 2) || (objc > 4)) {
1042 return TclX_WrongArgs (interp, objv [0],
1043 "listvar ?key? ?retvar | {}?");
1044 }
1045
1046 /*
1047 * Handle request for list of keys, use keylkeys command.
1048 */
1049 if (objc == 2)
1050 return TclX_KeylkeysObjCmd (clientData, interp, objc, objv);
1051
1052 keylPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
1053 if (keylPtr == NULL) {
1054 return TCL_ERROR;
1055 }
1056
1057 /*
1058 * Handle retrieving a value for a specified key.
1059 */
1060 key = Tcl_GetStringFromObj (objv [2], &keyLen);
1061 if (ValidateKey(interp, key, keyLen) == TCL_ERROR) {
1062 return TCL_ERROR;
1063 }
1064
1065 status = TclX_KeyedListGet (interp, keylPtr, key, &valuePtr);
1066 if (status == TCL_ERROR)
1067 return TCL_ERROR;
1068
1069 /*
1070 * Handle key not found.
1071 */
1072 if (status == TCL_BREAK) {
1073 if (objc == 3) {
1074 TclX_AppendObjResult (interp, "key \"", key,
1075 "\" not found in keyed list", (char *) NULL);
1076 return TCL_ERROR;
1077 } else {
1078 Tcl_SetBooleanObj (Tcl_GetObjResult (interp), FALSE);
1079 return TCL_OK;
1080 }
1081 }
1082
1083 /*
1084 * No variable specified, so return value in the result.
1085 */
1086 if (objc == 3) {
1087 Tcl_SetObjResult (interp, valuePtr);
1088 return TCL_OK;
1089 }
1090
1091 /*
1092 * Variable (or empty variable name) specified.
1093 */
1094 if (!TclX_IsNullObj(objv [3]) &&
1095 (Tcl_ObjSetVar2(interp, objv [3], NULL, valuePtr,
1096 TCL_LEAVE_ERR_MSG) == NULL)) {
1097 return TCL_ERROR;
1098 }
1099 Tcl_SetBooleanObj (Tcl_GetObjResult (interp), TRUE);
1100 return TCL_OK;
1101 }
1102
1103 /*-----------------------------------------------------------------------------
1104 * Tcl_KeylsetObjCmd --
1105 * Implements the TCL keylset command:
1106 * keylset listvar key value ?key value...?
1107 *-----------------------------------------------------------------------------
1108 */
1109 static int
TclX_KeylsetObjCmd(clientData,interp,objc,objv)1110 TclX_KeylsetObjCmd (clientData, interp, objc, objv)
1111 ClientData clientData;
1112 Tcl_Interp *interp;
1113 int objc;
1114 Tcl_Obj *CONST objv[];
1115 {
1116 Tcl_Obj *keylVarPtr, *newVarObj;
1117 char *key;
1118 int idx, keyLen, result = TCL_OK;
1119
1120 if ((objc < 4) || ((objc % 2) != 0)) {
1121 return TclX_WrongArgs (interp, objv [0],
1122 "listvar key value ?key value...?");
1123 }
1124
1125 /*
1126 * Get the variable that we are going to update. If the var doesn't exist,
1127 * create it. If it is shared by more than being a variable, duplicated
1128 * it.
1129 */
1130 keylVarPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
1131 if (keylVarPtr == NULL) {
1132 newVarObj = keylVarPtr = TclX_NewKeyedListObj();
1133 Tcl_IncrRefCount(newVarObj);
1134 } else if (Tcl_IsShared(keylVarPtr)) {
1135 newVarObj = keylVarPtr = Tcl_DuplicateObj(keylVarPtr);
1136 Tcl_IncrRefCount(newVarObj);
1137 } else {
1138 newVarObj = NULL;
1139 }
1140
1141 for (idx = 2; idx < objc; idx += 2) {
1142 key = Tcl_GetStringFromObj (objv [idx], &keyLen);
1143 if ((ValidateKey(interp, key, keyLen) == TCL_ERROR)
1144 || (TclX_KeyedListSet (interp, keylVarPtr, key, objv [idx+1])
1145 != TCL_OK)) {
1146 result = TCL_ERROR;
1147 break;
1148 }
1149 }
1150
1151 if ((result == TCL_OK) &&
1152 (Tcl_ObjSetVar2(interp, objv[1], NULL, keylVarPtr,
1153 TCL_LEAVE_ERR_MSG) == NULL)) {
1154 result = TCL_ERROR;
1155 }
1156
1157 if (newVarObj != NULL) {
1158 Tcl_DecrRefCount(newVarObj);
1159 }
1160 return result;
1161 }
1162
1163 /*-----------------------------------------------------------------------------
1164 * Tcl_KeyldelObjCmd --
1165 * Implements the TCL keyldel command:
1166 * keyldel listvar key ?key ...?
1167 *----------------------------------------------------------------------------
1168 */
1169 static int
TclX_KeyldelObjCmd(clientData,interp,objc,objv)1170 TclX_KeyldelObjCmd (clientData, interp, objc, objv)
1171 ClientData clientData;
1172 Tcl_Interp *interp;
1173 int objc;
1174 Tcl_Obj *CONST objv[];
1175 {
1176 Tcl_Obj *keylVarPtr, *keylPtr;
1177 char *key;
1178 int idx, keyLen, status;
1179
1180 if (objc < 3) {
1181 return TclX_WrongArgs (interp, objv [0], "listvar key ?key ...?");
1182 }
1183
1184 /*
1185 * Get the variable that we are going to update. If it is shared by more
1186 * than being a variable, duplicated it.
1187 */
1188 keylVarPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
1189 if (keylVarPtr == NULL) {
1190 return TCL_ERROR;
1191 }
1192 if (Tcl_IsShared (keylVarPtr)) {
1193 keylPtr = Tcl_DuplicateObj (keylVarPtr);
1194 keylVarPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, keylPtr,
1195 TCL_LEAVE_ERR_MSG);
1196 if (keylVarPtr == NULL) {
1197 Tcl_DecrRefCount(keylPtr);
1198 return TCL_ERROR;
1199 }
1200 if (keylVarPtr != keylPtr)
1201 Tcl_DecrRefCount(keylPtr);
1202 }
1203 keylPtr = keylVarPtr;
1204
1205 for (idx = 2; idx < objc; idx++) {
1206 key = Tcl_GetStringFromObj (objv [idx], &keyLen);
1207 if (ValidateKey(interp, key, keyLen) == TCL_ERROR) {
1208 return TCL_ERROR;
1209 }
1210
1211 status = TclX_KeyedListDelete (interp, keylPtr, key);
1212 switch (status) {
1213 case TCL_BREAK:
1214 TclX_AppendObjResult (interp, "key not found: \"",
1215 key, "\"", (char *) NULL);
1216 return TCL_ERROR;
1217 case TCL_ERROR:
1218 return TCL_ERROR;
1219 }
1220 }
1221
1222 return TCL_OK;
1223 }
1224
1225 /*-----------------------------------------------------------------------------
1226 * Tcl_KeylkeysObjCmd --
1227 * Implements the TCL keylkeys command:
1228 * keylkeys listvar ?key?
1229 *-----------------------------------------------------------------------------
1230 */
1231 static int
TclX_KeylkeysObjCmd(clientData,interp,objc,objv)1232 TclX_KeylkeysObjCmd (clientData, interp, objc, objv)
1233 ClientData clientData;
1234 Tcl_Interp *interp;
1235 int objc;
1236 Tcl_Obj *CONST objv[];
1237 {
1238 Tcl_Obj *keylPtr, *listObjPtr;
1239 char *key;
1240 int keyLen, status;
1241
1242 if ((objc < 2) || (objc > 3)) {
1243 return TclX_WrongArgs (interp, objv [0], "listvar ?key?");
1244 }
1245
1246 keylPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
1247 if (keylPtr == NULL) {
1248 return TCL_ERROR;
1249 }
1250
1251 /*
1252 * If key argument is not specified, then objv [2] is NULL or empty,
1253 * meaning get top level keys.
1254 */
1255 if (objc < 3) {
1256 key = NULL;
1257 } else {
1258 key = Tcl_GetStringFromObj (objv [2], &keyLen);
1259 if (ValidateKey(interp, key, keyLen) == TCL_ERROR) {
1260 return TCL_ERROR;
1261 }
1262 }
1263
1264 status = TclX_KeyedListGetKeys (interp, keylPtr, key, &listObjPtr);
1265 switch (status) {
1266 case TCL_BREAK:
1267 TclX_AppendObjResult (interp, "key not found: \"", key, "\"",
1268 (char *) NULL);
1269 return TCL_ERROR;
1270 case TCL_ERROR:
1271 return TCL_ERROR;
1272 }
1273
1274 Tcl_SetObjResult (interp, listObjPtr);
1275
1276 return TCL_OK;
1277 }
1278
1279 /*-----------------------------------------------------------------------------
1280 * TclX_KeyedListInit --
1281 * Initialize the keyed list commands for this interpreter.
1282 *
1283 * Parameters:
1284 * o interp - Interpreter to add commands to.
1285 *-----------------------------------------------------------------------------
1286 */
1287 void
TclX_KeyedListInit(interp)1288 TclX_KeyedListInit (interp)
1289 Tcl_Interp *interp;
1290 {
1291 Tcl_RegisterObjType (&keyedListType);
1292
1293 Tcl_CreateObjCommand (interp, "keylget", TclX_KeylgetObjCmd,
1294 (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
1295
1296 Tcl_CreateObjCommand (interp, "keylset", TclX_KeylsetObjCmd,
1297 (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
1298
1299 Tcl_CreateObjCommand (interp, "keyldel", TclX_KeyldelObjCmd,
1300 (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
1301
1302 Tcl_CreateObjCommand (interp, "keylkeys", TclX_KeylkeysObjCmd,
1303 (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
1304 }
1305
1306
1307