Bug Summary

File:d/tclxkeylist.c
Warning:line 1079, column 13
Duplicate code detected
Note:line 1117, column 21
Similar code here

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tclxkeylist.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c tclxkeylist.c
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 */
16
17/*
18 * tclxkeylist.c --
19 *
20 * Keyed list support, modified from the original
21 * Tcl8.x based TclX and Tcl source.
22 *
23 */
24
25#include "nsd.h"
26
27static int TclX_WrongArgs(Tcl_Interp *interp, Tcl_Obj *commandNameObj, const char *msg);
28static bool_Bool TclX_IsNullObj(Tcl_Obj *objPtr) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
29
30
31
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
35/* Stuff copied from the rest of TclX to avoid dependencies */
36/*---------------------------------------------------------------------------*/
37/*---------------------------------------------------------------------------*/
38
39
40#define TRUE1 1
41#define FALSE0 0
42
43
44#define ckbinstrdup(a, b)((char *)memcpy(((void *) Tcl_Alloc((unsigned)((unsigned int)
((b)+1)))), (a), (size_t)((b)+1)))
\
45 ((char *)memcpy(ckalloc((unsigned int)((b)+1))((void *) Tcl_Alloc((unsigned)((unsigned int)((b)+1)))), (a), (size_t)((b)+1)))
46
47/*
48 * listType is used in TclX_IsNullObj() in read-only mode
49 * therefore no need to mutex protect them (see below).
50 */
51static const Tcl_ObjType *listType;
52
53/*
54 * This is called once from InitInterp() call in tclinit.c
55 * for first-time initialization of special Tcl_Objs.
56 */
57void NsTclInitKeylistType(void)
58{
59 listType = Tcl_GetObjType("list");
60}
61
62/*-----------------------------------------------------------------------------
63 * TclX_WrongArgs --
64 *
65 * Easily create "wrong # args" error messages.
66 *
67 * Parameters:
68 * - commandNameObj - Object containing name of command (objv[0])
69 * - msg - Text message to append.
70 * Returns:
71 * TCL_ERROR
72 *-----------------------------------------------------------------------------
73 */
74static int
75TclX_WrongArgs(Tcl_Interp *interp, Tcl_Obj *commandNameObj, const char *msg)
76{
77 const char *commandName = Tcl_GetString(commandNameObj);
78
79 Ns_TclPrintfResult(interp, "wrong # args: %s %s",
80 commandName,
81 (*msg != '\0') ? msg : NS_EMPTY_STRING);
82 return TCL_ERROR1;
83}
84
85/*-----------------------------------------------------------------------------
86 * TclX_IsNullObj --
87 *
88 * Check if an object is {}, either in list or zero-length string form, with
89 * out forcing a conversion.
90 *
91 * Parameters:
92 * - objPtr - Object to check.
93 * Returns:
94 * NS_TRUE if NULL, NS_FALSE if not.
95 *-----------------------------------------------------------------------------
96 */
97static bool_Bool
98TclX_IsNullObj(Tcl_Obj *objPtr)
99{
100 bool_Bool success;
101
102 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
103
104 if (objPtr->typePtr == NULL((void*)0)) {
105 success = (objPtr->length == 0);
106 } else if (objPtr->typePtr == listType) {
107 int length = 0;
108
109 (void) Tcl_ListObjLength(NULL((void*)0), objPtr, &length);
110 success = (length == 0);
111 } else {
112 success = (Tcl_GetCharLength(objPtr) == 0);
113 }
114 return success;
115}
116
117
118/*
119 *-----------------------------------------------------------------------------
120 *
121 * Exported C-API interface to keyed lists.
122 *
123 *-----------------------------------------------------------------------------
124 */
125
126Tcl_Obj*
127TclX_NewKeyedListObj(void);
128
129int
130TclX_KeyedListGet(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key,
131 Tcl_Obj **valuePtrPtr);
132int
133TclX_KeyedListSet(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key,
134 Tcl_Obj *valuePtr);
135int
136TclX_KeyedListDelete(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key);
137
138int
139TclX_KeyedListGetKeys(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key,
140 Tcl_Obj **listObjPtrPtr);
141
142
143/*---------------------------------------------------------------------------*/
144/*---------------------------------------------------------------------------*/
145/* Here is the C-API compatibility layer */
146/* for those who still use it (AOL) */
147/*---------------------------------------------------------------------------*/
148/*---------------------------------------------------------------------------*/
149
150/*
151 * ----------------------------------------------------------------------------
152 * -
153 *
154 * Tcl_GetKeyedListKeys -- Retrieve a list of keys from a keyed list. The list
155 * is walked rather than converted to an argv for increased performance.
156 *
157 * Parameters:
158
159 * - interp (I/O) - Error message will be return in result if there
160 * is an error.
161 * - subFieldName (I) - If "" or NULL, then the keys are
162 * retreved for the top level of the list. If specified, it is name of the
163 * field who's subfield keys are to be retrieve.
164 * - keyedList (I) - The list to search for the field.
165 * - keysArgcPtr (O) - The number of keys in the keyed list is returned here.
166 * - keysArgvPtr (O) - An argv containing the key names. It is dynamically
167 * allocated, containing both the array and the strings. A single call
168 * to ckfree will release it.
169 * Returns:
170 * TCL_OK - If the field was found.
171 * TCL_BREAK - If the field was not found.
172 * TCL_ERROR - If an error occurred.
173 * ---------------------------------------------------------------------------
174 */
175
176int
177Tcl_GetKeyedListKeys(Tcl_Interp *interp, const char *subFieldName, const char *keyedList,
178 int *keysArgcPtr, char ***keysArgvPtr)
179{
180 Tcl_Obj *keylistPtr = Tcl_NewStringObj(keyedList, -1);
181 const char *keylistKey = subFieldName;
182 Tcl_Obj *objValPtr;
183 int status;
184
185 Tcl_IncrRefCount(keylistPtr)++(keylistPtr)->refCount;
186
187 status = TclX_KeyedListGetKeys(interp, keylistPtr, keylistKey, &objValPtr);
188
189 if (status == TCL_BREAK3) {
190 if (keysArgcPtr != NULL((void*)0)) {
191 *keysArgcPtr = 0;
192 }
193 if (keysArgvPtr != NULL((void*)0)) {
194 *keysArgvPtr = NULL((void*)0);
195 }
196 } else if (status == TCL_OK0) {
197 if (keysArgcPtr != NULL((void*)0) && keysArgvPtr != NULL((void*)0)) {
198 int keySize, sumKeySize = 0;
199 int ii, keyCount;
200 char **keyArgv, *nextByte;
201 Tcl_Obj **objValues;
202
203 if (Tcl_ListObjGetElements(interp, objValPtr, &keyCount,
204 &objValues) != TCL_OK0) {
205 Tcl_DecrRefCount(keylistPtr)do { Tcl_Obj *_objPtr = (keylistPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
206 return TCL_ERROR1;
207 }
208 for (ii = 0; ii < keyCount; ii++) {
209 sumKeySize += Tcl_GetCharLength(objValues[ii]) + 1;
210 }
211 keySize = (keyCount + 1) * (int)sizeof(char *);
212 keyArgv = (char **)ckalloc((unsigned int)keySize + (unsigned int)sumKeySize)((void *) Tcl_Alloc((unsigned)((unsigned int)keySize + (unsigned
int)sumKeySize)))
;
213 keyArgv[keyCount] = NULL((void*)0);
214 nextByte = ((char *)keyArgv) + keySize;
215
216 for (ii = 0; ii < keyCount; ii++) {
217 const char *keyPtr;
218 int keyLen = 0;
219
220 keyArgv[ii] = nextByte;
221 keyPtr = Tcl_GetStringFromObj(objValues[ii], &keyLen);
222 memcpy(nextByte, keyPtr, (size_t)keyLen);
223 nextByte[keyLen] = '\0';
224 nextByte += keyLen + 1;
225 }
226 *keysArgcPtr = keyCount;
227 *keysArgvPtr = keyArgv;
228 }
229 Tcl_DecrRefCount(objValPtr)do { Tcl_Obj *_objPtr = (objValPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
230 }
231
232 Tcl_DecrRefCount(keylistPtr)do { Tcl_Obj *_objPtr = (keylistPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
233
234 return status;
235}
236
237/*
238 * ----------------------------------------------------------------------------
239 * -
240 *
241 * Tcl_GetKeyedListField -- Retrieve a field value from a keyed list. The list
242 * is walked rather than converted to an argv for increased performance. This
243 * if the name contains sub-fields, this function recursive.
244 *
245 * Parameters:
246 * - interp (I/O) - Error message will be return in result if there
247 * is an error.
248 * - fieldName (I) - The name of the field to extract. Will recursively
249 * process sub-field names separated by `.'.
250 * - keyedList (I) - The list to search for the field.
251 * - fieldValuePtr (O) - If the field is found, a pointer to a dynamically
252 * allocated string containing the value is returned here.
253 * If NULL is specified, then only the presence of the field is
254 * validated, the value is not returned.
255 * Returns:
256 * TCL_OK - If the field was found.
257 * TCL_BREAK - If the field was not found.
258 * TCL_ERROR - If an error occurred.
259 * ---------------------------------------------------------------------------
260 * -- */
261
262int
263Tcl_GetKeyedListField(Tcl_Interp *interp, const char *fieldName,
264 const char *keyedList, char **fieldValuePtr)
265{
266 Tcl_Obj *keylistPtr = Tcl_NewStringObj(keyedList, -1);
267 const char *keylistKey = fieldName;
268
269 Tcl_Obj *objValPtr;
270 int status;
271
272 Tcl_IncrRefCount(keylistPtr)++(keylistPtr)->refCount;
273
274 status = TclX_KeyedListGet(interp, keylistPtr, keylistKey, &objValPtr);
275
276 if (status == TCL_BREAK3) {
277 if (fieldValuePtr != NULL((void*)0)) {
278 *fieldValuePtr = NULL((void*)0);
279 }
280 } else if (status == TCL_OK0) {
281 if (fieldValuePtr != NULL((void*)0)) {
282 int valueLen;
283 const char *keyValue = Tcl_GetStringFromObj(objValPtr, &valueLen);
284 char *newValue = ns_strncopy(keyValue, (ssize_t)valueLen);
285
286 *fieldValuePtr = newValue;
287 }
288 }
289
290 Tcl_DecrRefCount(keylistPtr)do { Tcl_Obj *_objPtr = (keylistPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
291
292 return status;
293}
294
295/*
296 * ----------------------------------------------------------------------------
297 * -
298 *
299 * Tcl_SetKeyedListField -- Set a field value in keyed list.
300 *
301 * Parameters:
302 * - interp (I/O) - Error message will be return in result if there
303 * is an error.
304 * - fieldName (I) - The name of the field to extract. Will
305 * recursively process sub-field names separated by `.'.
306 * - fieldValue (I) - The value to set for the field.
307 * - keyedList (I) - The keyed list to set a field value in, may be
308 * a NULL or an empty list to create a new keyed list.
309 * Returns:
310 * A pointer to a dynamically allocated string, or
311 * NULL if an error occurred.
312 * ---------------------------------------------------------------------------
313 * -- */
314
315char *
316Tcl_SetKeyedListField(Tcl_Interp *interp, const char *fieldName,
317 const char *fieldValue, const char *keyedList)
318{
319 Tcl_Obj *keylistPtr = Tcl_NewStringObj(keyedList, -1);
320 Tcl_Obj *valuePtr = Tcl_NewStringObj(fieldValue, -1);
321 const char *keylistKey = fieldName;
322 char *result = NULL((void*)0);
323 int status, listLen;
324
325 Tcl_IncrRefCount(keylistPtr)++(keylistPtr)->refCount;
326 Tcl_IncrRefCount(valuePtr)++(valuePtr)->refCount;
327
328 status = TclX_KeyedListSet(interp, keylistPtr, keylistKey, valuePtr);
329
330 if (status == TCL_OK0) {
331 const char *listStr;
332
333 listStr = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &listLen);
334 result = ns_strncopy(listStr, (ssize_t)listLen);
335 }
336
337 Tcl_DecrRefCount(valuePtr)do { Tcl_Obj *_objPtr = (valuePtr); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
338 Tcl_DecrRefCount(keylistPtr)do { Tcl_Obj *_objPtr = (keylistPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
339
340 return result;
341}
342
343/*
344 * ----------------------------------------------------------------------------
345 * -
346 *
347 * Tcl_DeleteKeyedListField -- Delete a field value in keyed list.
348 *
349 * Parameters:
350 * - interp (I/O) - Error message will be return in result if there
351 * is an error.
352 * - fieldName (I) - The name of the field to extract. Will recursively
353 * process sub-field names separated by `.'.
354 * - fieldValue (I) - The value to set for the field.
355 * - keyedList (I) - The keyed list to delete the field from.
356 * Returns:
357 * A pointer to a dynamically allocated string containing the new list, or
358 * NULL if an error occurred.
359 * ---------------------------------------------------------------------------
360 * -- */
361
362char *
363Tcl_DeleteKeyedListField(Tcl_Interp *interp, const char *fieldName, const char *keyedList)
364{
365 Tcl_Obj *keylistPtr = Tcl_NewStringObj(keyedList, -1);
366 char *newList = NULL((void*)0);
367 int status, listLen;
368
369 Tcl_IncrRefCount(keylistPtr)++(keylistPtr)->refCount;
370 status = TclX_KeyedListDelete(interp, keylistPtr, fieldName);
371
372 if (status == TCL_OK0) {
373 const char *listStr;
374
375 listStr = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &listLen);
376 newList = ns_strncopy(listStr, (ssize_t)listLen);
377 }
378
379 Tcl_DecrRefCount(keylistPtr)do { Tcl_Obj *_objPtr = (keylistPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
380
381 return newList;
382}
383
384/*---------------------------------------------------------------------------*/
385/*---------------------------------------------------------------------------*/
386/* Here is where the original file begins */
387/*---------------------------------------------------------------------------*/
388/*---------------------------------------------------------------------------*/
389
390/*
391 * Keyed lists are stored as arrays recursively defined objects. The data
392 * portion of a keyed list entry is a Tcl_Obj which may be a keyed list object
393 * or any other Tcl_Obj. Since determine the structure of a keyed list is
394 * lazy (you don't know if an element is data or another keyed list) until it
395 * is accessed, the object can be transformed into a keyed list from a Tcl
396 * string or list.
397 */
398
399/*
400 * An entry in a keyed list array. (FIX: Should key be object?)
401 */
402typedef struct {
403 char *key;
404 Tcl_Obj *valuePtr;
405} keylEntry_t;
406
407/*
408 * Internal representation of a keyed list object.
409 */
410typedef struct {
411 int arraySize; /* Current slots available in the array. */
412 int numEntries; /* Number of actual entries in the array. */
413 keylEntry_t *entries; /* Array of keyed list entries. */
414} keylIntObj_t;
415
416/*
417 * Amount to increment array size by when it needs to grow.
418 */
419#define KEYEDLIST_ARRAY_INCR_SIZE16 16
420
421/*
422 * Macros to validate a keyed list object or internal representation
423 */
424#ifdef TCLX_DEBUG
425# define KEYL_OBJ_ASSERT(keylAPtr) {\
426 assert(keylAPtr->typePtr == &keyedListType)((void) (0)); \
427 ValidateKeyedList(keylAIntPtr); \
428 }
429# define KEYL_REP_ASSERT(keylAIntPtr) \
430 ValidateKeyedList(keylAIntPtr)
431#else
432# define KEYL_REP_ASSERT(keylAIntPtr)
433#endif
434
435
436/*
437 * Prototypes of internal functions.
438 */
439static void DupSharedKeyListChild(const keylIntObj_t *keylIntPtr, int idx) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
440
441#ifdef TCLX_DEBUG
442static void
443ValidateKeyedList(keylIntObj_t *keylIntPtr);
444#endif
445
446static int
447ValidateKey(Tcl_Interp *interp, const char *key, int keyLen, int isPath);
448
449static keylIntObj_t *
450AllocKeyedListIntRep(void);
451
452static void
453FreeKeyedListData(keylIntObj_t *keylIntPtr);
454
455static void
456EnsureKeyedListSpace(keylIntObj_t *keylIntPtr,
457 int newNumEntries);
458
459static void
460DeleteKeyedListEntry(keylIntObj_t *keylIntPtr,
461 int entryIdx);
462
463static int
464FindKeyedListEntry(const keylIntObj_t *keylIntPtr,
465 const char *key,
466 size_t *keyLenPtr,
467 const char **nextSubKeyPtr);
468
469static int
470ObjToKeyedListEntry(Tcl_Interp *interp,
471 Tcl_Obj *objPtr,
472 keylEntry_t *entryPtr);
473
474
475static Tcl_FreeInternalRepProc FreeKeyedListInternalRep;
476static Tcl_DupInternalRepProc DupKeyedListInternalRep;
477static Tcl_UpdateStringProc UpdateStringOfKeyedList;
478static Tcl_SetFromAnyProc SetKeyedListFromAny;
479
480/*
481 * Type definition.
482 */
483static const Tcl_ObjType keyedListType = {
484 "keyedList", /* name */
485 FreeKeyedListInternalRep, /* freeIntRepProc */
486 DupKeyedListInternalRep, /* dupIntRepProc */
487 UpdateStringOfKeyedList, /* updateStringProc */
488 SetKeyedListFromAny /* setFromAnyProc */
489};
490
491
492/*-----------------------------------------------------------------------------
493 * DupSharedKeyListChild --
494 * duplicate a child entry of a keyed list if it is share by more
495 * than the parent.
496 * Parameters:
497 * keylIntPtr - Keyed list internal representation.
498 * idx - Index position
499 *-----------------------------------------------------------------------------
500 */
501static void
502DupSharedKeyListChild(const keylIntObj_t *keylIntPtr, int idx)
503{
504 NS_NONNULL_ASSERT(keylIntPtr != NULL)((void) (0));
505
506 if (Tcl_IsShared(keylIntPtr->entries[idx].valuePtr)((keylIntPtr->entries[idx].valuePtr)->refCount > 1)) {
507 keylIntPtr->entries[idx].valuePtr =
508 Tcl_DuplicateObj(keylIntPtr->entries[idx].valuePtr);
509 Tcl_IncrRefCount(keylIntPtr->entries[idx].valuePtr)++(keylIntPtr->entries[idx].valuePtr)->refCount;
510 }
511}
512
513
514/*-----------------------------------------------------------------------------
515 * ValidateKeyedList --
516 * Validate a keyed list (only when TCLX_DEBUG is enabled).
517 * Parameters:
518 * o keylIntPtr - Keyed list internal representation.
519 *-----------------------------------------------------------------------------
520 */
521#ifdef TCLX_DEBUG
522static void
523ValidateKeyedList(keylIntPtr)
524 keylIntObj_t *keylIntPtr;
525{
526 int idx;
527
528 assert(keylIntPtr->arraySize >= keylIntPtr->numEntries)((void) (0));
529 assert(keylIntPtr->arraySize >= 0)((void) (0));
530 assert(keylIntPtr->numEntries >= 0)((void) (0));
531 assert((keylIntPtr->arraySize > 0) ?((void) (0))
532 (keylIntPtr->entries != NULL) : TRUE)((void) (0));
533 assert((keylIntPtr->numEntries > 0) ?((void) (0))
534 (keylIntPtr->entries != NULL) : TRUE)((void) (0));
535
536 for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
537 keylEntry_t *entryPtr = &(keylIntPtr->entries[idx]);
538 assert(entryPtr->key != NULL)((void) (0));
539 assert(entryPtr->valuePtr->refCount >= 1)((void) (0));
540 if (entryPtr->valuePtr->typePtr == &keyedListType) {
541 ValidateKeyedList(entryPtr->valuePtr->internalRep.otherValuePtr);
542 }
543 }
544}
545#endif
546
547/*-----------------------------------------------------------------------------
548 * ValidateKey --
549 * Check that a key or keypath string is a valid value.
550 *
551 * Parameters:
552 * o interp - Used to return error messages.
553 * o key - Key string to check.
554 * o keyLen - Length of the string, used to check for binary data.
555 * o isPath - TRUE if this is a key path, FALSE if its a simple key and
556 * thus "." is illegal.
557 * Returns:
558 * TCL_OK or TCL_ERROR.
559 *-----------------------------------------------------------------------------
560 */
561static int
562ValidateKey(Tcl_Interp *interp, const char *key, int keyLen, int isPath)
563{
564 int result = TCL_OK0;
565
566 if (strlen (key) != (size_t) keyLen) {
567 Ns_TclPrintfResult(interp, "keyed list key may not be a binary string");
568 result = TCL_ERROR1;
569
570 } else if (key[0] == '\0') {
571 Ns_TclPrintfResult(interp, "keyed list key may not be an empty string");
572 result = TCL_ERROR1;
573
574 } else {
575 const char *keyp;
576
577 for (keyp = key; *keyp != '\0'; keyp++) {
578 if ((isPath == 0) && (*keyp == '.')) {
579 Ns_TclPrintfResult(interp,
580 "keyed list key may not contain a \".\"; "
581 "it is used as a separator in key paths");
582 result = TCL_ERROR1;
583 break;
584 }
585 }
586 }
587 return result;
588}
589
590
591/*-----------------------------------------------------------------------------
592 * AllocKeyedListIntRep --
593 * Allocate an and initialize the keyed list internal representation.
594 *
595 * Returns:
596 * A pointer to the keyed list internal structure.
597 *-----------------------------------------------------------------------------
598 */
599static keylIntObj_t *
600AllocKeyedListIntRep(void)
601{
602 keylIntObj_t *keylIntPtr;
603
604 keylIntPtr = (keylIntObj_t *) ckalloc(sizeof(keylIntObj_t))((void *) Tcl_Alloc((unsigned)(sizeof(keylIntObj_t))));
605
606 keylIntPtr->arraySize = 0;
607 keylIntPtr->numEntries = 0;
608 keylIntPtr->entries = NULL((void*)0);
609
610 return keylIntPtr;
611}
612
613/*-----------------------------------------------------------------------------
614 * FreeKeyedListData --
615 * Free the internal representation of a keyed list.
616 *
617 * Parameters:
618 * o keylIntPtr - Keyed list internal structure to free.
619 *-----------------------------------------------------------------------------
620 */
621static void
622FreeKeyedListData(keylIntObj_t * keylIntPtr)
623{
624 int idx;
625
626 for (idx = 0; idx < keylIntPtr->numEntries ; idx++) {
627 ns_free(keylIntPtr->entries[idx].key);
628 Tcl_DecrRefCount(keylIntPtr->entries[idx].valuePtr)do { Tcl_Obj *_objPtr = (keylIntPtr->entries[idx].valuePtr
); if (_objPtr->refCount-- <= 1) { TclFreeObj(_objPtr);
} } while(0)
;
629 }
630 if (keylIntPtr->entries != NULL((void*)0)) {
631 ns_free((char *) keylIntPtr->entries);
632 }
633 ckfree((char *) keylIntPtr)Tcl_Free((char *)((char *) keylIntPtr));
634}
635
636/*-----------------------------------------------------------------------------
637 * EnsureKeyedListSpace --
638 * Ensure there is enough room in a keyed list array for a certain number
639 * of entries, expanding if necessary.
640 *
641 * Parameters:
642 * o keylIntPtr - Keyed list internal representation.
643 * o newNumEntries - The number of entries that are going to be added to
644 * the keyed list.
645 *-----------------------------------------------------------------------------
646 */
647static void
648EnsureKeyedListSpace(keylIntObj_t * keylIntPtr, int newNumEntries)
649{
650 KEYL_REP_ASSERT(keylIntPtr);
651
652 if ((keylIntPtr->arraySize - keylIntPtr->numEntries) < newNumEntries) {
653 int newSize = keylIntPtr->arraySize + newNumEntries +
654 KEYEDLIST_ARRAY_INCR_SIZE16;
655 if (keylIntPtr->entries == NULL((void*)0)) {
656 keylIntPtr->entries = (keylEntry_t *)
657 ns_malloc((unsigned)newSize * (unsigned)sizeof(keylEntry_t));
658 } else {
659 keylIntPtr->entries = (keylEntry_t *)
660 ns_realloc((char *) keylIntPtr->entries,
661 (unsigned)newSize * sizeof(keylEntry_t));
662 }
663 keylIntPtr->arraySize = newSize;
664 }
665
666 KEYL_REP_ASSERT(keylIntPtr);
667}
668
669/*-----------------------------------------------------------------------------
670 * DeleteKeyedListEntry --
671 * Delete an entry from a keyed list.
672 *
673 * Parameters:
674 * o keylIntPtr - Keyed list internal representation.
675 * o entryIdx - Index of entry to delete.
676 *-----------------------------------------------------------------------------
677 */
678static void
679DeleteKeyedListEntry(keylIntObj_t *keylIntPtr, int entryIdx)
680{
681 int idx;
682
683 ns_free(keylIntPtr->entries[entryIdx].key);
684 Tcl_DecrRefCount(keylIntPtr->entries[entryIdx].valuePtr)do { Tcl_Obj *_objPtr = (keylIntPtr->entries[entryIdx].valuePtr
); if (_objPtr->refCount-- <= 1) { TclFreeObj(_objPtr);
} } while(0)
;
685
686 for (idx = entryIdx; idx < keylIntPtr->numEntries - 1; idx++) {
687 keylIntPtr->entries[idx] = keylIntPtr->entries[idx + 1];
688 }
689 keylIntPtr->numEntries--;
690
691 KEYL_REP_ASSERT(keylIntPtr);
692}
693
694/*-----------------------------------------------------------------------------
695 * FindKeyedListEntry --
696 * Find an entry in keyed list.
697 *
698 * Parameters:
699 * o keylIntPtr - Keyed list internal representation.
700 * o key - Name of key to search for.
701 * o keyLenPtr - In not NULL, the length of the key for this
702 * level is returned here. This excludes subkeys and the `.' delimiters.
703 * o nextSubKeyPtr - If not NULL, the start of the name of the next
704 * sub-key within key is returned.
705 * Returns:
706 * Index of the entry or -1 if not found.
707 *-----------------------------------------------------------------------------
708 */
709static int
710FindKeyedListEntry(const keylIntObj_t *keylIntPtr, const char *key, size_t *keyLenPtr, const char **nextSubKeyPtr)
711{
712 const char *keySeparPtr;
713 size_t keyLen;
714 int findIdx;
715
716 keySeparPtr = strchr(key, INTCHAR('.')((int)((unsigned char)(('.')))));
717 if (keySeparPtr != NULL((void*)0)) {
718 keyLen = (size_t)(keySeparPtr - key);
719 } else {
720 keyLen = strlen(key);
721 }
722
723 for (findIdx = 0; findIdx < keylIntPtr->numEntries; findIdx++) {
724 if ((strncmp(keylIntPtr->entries[findIdx].key, key, keyLen) == 0)
725 && keylIntPtr->entries[findIdx].key[keyLen] == '\0') {
726 break;
727 }
728 }
729
730 if (nextSubKeyPtr != NULL((void*)0)) {
731 if (keySeparPtr == NULL((void*)0)) {
732 *nextSubKeyPtr = NULL((void*)0);
733 } else {
734 *nextSubKeyPtr = keySeparPtr + 1;
735 }
736 }
737 if (keyLenPtr != NULL((void*)0)) {
738 *keyLenPtr = keyLen;
739 }
740
741 if (findIdx >= keylIntPtr->numEntries) {
742 findIdx = -1;
743 }
744
745 return findIdx;
746}
747
748/*-----------------------------------------------------------------------------
749 * ObjToKeyedListEntry --
750 * Convert an object to a keyed list entry. (Keyword/value pair).
751 *
752 * Parameters:
753 * o interp - Used to return error messages, if not NULL.
754 * o objPtr - Object to convert. Each entry must be a two element list,
755 * with the first element being the key and the second being the
756 * value.
757 * o entryPtr - The keyed list entry to initialize from the object.
758 * Returns:
759 * TCL_OK or TCL_ERROR.
760 *-----------------------------------------------------------------------------
761 */
762static int
763ObjToKeyedListEntry(Tcl_Interp *interp, Tcl_Obj *objPtr, keylEntry_t *entryPtr)
764{
765 int objc, result = TCL_OK0;
766 Tcl_Obj **objv;
767
768 if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK0) {
769 Ns_TclPrintfResult(interp, "keyed list entry not a valid list, "
770 "found \"%s\"",
771 Tcl_GetString(objPtr));
772 result = TCL_ERROR1;
773
774 } else if (objc != 2) {
775 Ns_TclPrintfResult(interp, "keyed list entry must be a two "
776 "element list, found \"%s\"",
777 Tcl_GetString(objPtr));
778 result = TCL_ERROR1;
779
780 } else {
781 const char *key;
782 int keyLen;
783
784 key = Tcl_GetStringFromObj(objv[0], &keyLen);
785 if (ValidateKey(interp, key, keyLen, FALSE0) == TCL_ERROR1) {
786 result = TCL_ERROR1;
787
788 } else {
789 entryPtr->key = ns_strdup(key);
790 entryPtr->valuePtr = Tcl_DuplicateObj(objv[1]);
791 Tcl_IncrRefCount(entryPtr->valuePtr)++(entryPtr->valuePtr)->refCount;
792 }
793 }
794 return result;
795}
796
797/*-----------------------------------------------------------------------------
798 * FreeKeyedListInternalRep --
799 * Free the internal representation of a keyed list.
800 *
801 * Parameters:
802 * o keylPtr - Keyed list object being deleted.
803 *-----------------------------------------------------------------------------
804 */
805static void
806FreeKeyedListInternalRep(Tcl_Obj *objPtr)
807{
808 FreeKeyedListData((keylIntObj_t *)objPtr->internalRep.otherValuePtr);
809}
810
811/*-----------------------------------------------------------------------------
812 * DupKeyedListInternalRep --
813 * Duplicate the internal representation of a keyed list.
814 *
815 * Parameters:
816 * o srcPtr - Keyed list object to copy.
817 * o copyPtr - Target object to copy internal representation to.
818 *-----------------------------------------------------------------------------
819 */
820static void
821DupKeyedListInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr)
822{
823 const keylIntObj_t *srcIntPtr =
824 (const keylIntObj_t *) srcPtr->internalRep.otherValuePtr;
825 keylIntObj_t *copyIntPtr;
826 int idx;
827
828 KEYL_REP_ASSERT(srcIntPtr);
829
830 copyIntPtr = (keylIntObj_t *) ckalloc(sizeof(keylIntObj_t))((void *) Tcl_Alloc((unsigned)(sizeof(keylIntObj_t))));
831 copyIntPtr->arraySize = srcIntPtr->arraySize;
832 copyIntPtr->numEntries = srcIntPtr->numEntries;
833 copyIntPtr->entries = (keylEntry_t *)
834 ns_malloc((unsigned)copyIntPtr->arraySize * (unsigned)sizeof(keylEntry_t));
835
836 if (unlikely(copyIntPtr->entries == NULL)(__builtin_expect((copyIntPtr->entries == ((void*)0)), 0))) {
837 Ns_Fatal("nsd keyedList: out of memory");
838 }
839 for (idx = 0; idx < srcIntPtr->numEntries ; idx++) {
840 copyIntPtr->entries[idx].key =
841 ns_strdup(srcIntPtr->entries[idx].key);
842 copyIntPtr->entries[idx].valuePtr = srcIntPtr->entries[idx].valuePtr;
843 Tcl_IncrRefCount(copyIntPtr->entries[idx].valuePtr)++(copyIntPtr->entries[idx].valuePtr)->refCount;
844 }
845
846 copyPtr->internalRep.otherValuePtr = (void *) copyIntPtr;
847 copyPtr->typePtr = &keyedListType;
848
849 KEYL_REP_ASSERT(copyIntPtr);
850}
851
852/*-----------------------------------------------------------------------------
853 * SetKeyedListFromAny --
854 * Convert an object to a keyed list from its string representation. Only
855 * the first level is converted, as there is no way of knowing how far down
856 * the keyed list recurses until lower levels are accessed.
857 *
858 * Parameters:
859 * o objPtr - Object to convert to a keyed list.
860 *-----------------------------------------------------------------------------
861 */
862static int
863SetKeyedListFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
864{
865 int objc, result = TCL_OK0;
866 Tcl_Obj **objv;
867
868 if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK0) {
869 result = TCL_ERROR1;
870
871 } else {
872 int idx;
873 keylIntObj_t *keylIntPtr = AllocKeyedListIntRep();
874
875 EnsureKeyedListSpace(keylIntPtr, objc);
876
877 for (idx = 0; idx < objc; idx++) {
878 if (ObjToKeyedListEntry(interp, objv[idx],
879 &(keylIntPtr->entries[keylIntPtr->numEntries])) != TCL_OK0) {
880 result = TCL_ERROR1;
881 break;
882 }
883 keylIntPtr->numEntries++;
884 }
885
886 if (result == TCL_OK0) {
887 if ((objPtr->typePtr != NULL((void*)0)) &&
888 (objPtr->typePtr->freeIntRepProc != NULL((void*)0))) {
889 (*objPtr->typePtr->freeIntRepProc)(objPtr);
890 }
891 objPtr->internalRep.otherValuePtr = (void *) keylIntPtr;
892 objPtr->typePtr = &keyedListType;
893
894 KEYL_REP_ASSERT(keylIntPtr);
895 } else {
896 FreeKeyedListData(keylIntPtr);
897 }
898 }
899
900 return result;
901}
902
903/*-----------------------------------------------------------------------------
904 * UpdateStringOfKeyedList --
905 * Update the string representation of a keyed list.
906 *
907 * Parameters:
908 * o objPtr - Object to convert to a keyed list.
909 *-----------------------------------------------------------------------------
910 */
911static void
912UpdateStringOfKeyedList(Tcl_Obj *keylPtr)
913{
914#define UPDATE_STATIC_SIZE32 32
915 int idx, strLen;
916 Tcl_Obj **listObjv, *entryObjv[2], *tmpListObj;
917 Tcl_Obj *staticListObjv[UPDATE_STATIC_SIZE32];
918 const char *listStr;
919 const keylIntObj_t *keylIntPtr =
920 (const keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
921
922 /*
923 * Conversion to strings is done via list objects to support binary data.
924 */
925 if (keylIntPtr->numEntries > UPDATE_STATIC_SIZE32) {
926 listObjv = (Tcl_Obj **) ckalloc((unsigned)keylIntPtr->numEntries * (unsigned)sizeof(Tcl_Obj *))((void *) Tcl_Alloc((unsigned)((unsigned)keylIntPtr->numEntries
* (unsigned)sizeof(Tcl_Obj *))))
;
927 } else {
928 staticListObjv[0] = NULL((void*)0);
929 listObjv = staticListObjv;
930 }
931
932 /*
933 * Convert each keyed list entry to a two element list object. No
934 * need to incr/decr ref counts, the list objects will take care of that.
935 * FIX: Keeping key as string object will speed this up.
936 */
937 for (idx = 0; idx < keylIntPtr->numEntries; idx++) {
938 entryObjv[0] =
939 Tcl_NewStringObj(keylIntPtr->entries[idx].key,
940 (int)strlen(keylIntPtr->entries[idx].key));
941 entryObjv[1] = keylIntPtr->entries[idx].valuePtr;
942 listObjv[idx] = Tcl_NewListObj(2, entryObjv);
943 }
944
945 tmpListObj = Tcl_NewListObj(keylIntPtr->numEntries, listObjv);
946 listStr = Tcl_GetStringFromObj(tmpListObj, &strLen);
947 keylPtr->bytes = ckbinstrdup(listStr, strLen)((char *)memcpy(((void *) Tcl_Alloc((unsigned)((unsigned int)
((strLen)+1)))), (listStr), (size_t)((strLen)+1)))
;
948 keylPtr->length = strLen;
949
950 Tcl_DecrRefCount(tmpListObj)do { Tcl_Obj *_objPtr = (tmpListObj); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
951 if (listObjv != staticListObjv) {
952 ckfree((char *) listObjv)Tcl_Free((char *)((char *) listObjv));
953 }
954}
955
956/*-----------------------------------------------------------------------------
957 * TclX_NewKeyedListObj --
958 * Create and initialize a new keyed list object.
959 *
960 * Returns:
961 * A pointer to the object.
962 *-----------------------------------------------------------------------------
963 */
964Tcl_Obj *
965TclX_NewKeyedListObj(void)
966{
967 Tcl_Obj *keylPtr = Tcl_NewObj();
968 keylIntObj_t *keylIntPtr = AllocKeyedListIntRep();
969
970 keylPtr->internalRep.otherValuePtr = (void *) keylIntPtr;
971 keylPtr->typePtr = &keyedListType;
972 return keylPtr;
973}
974
975/*-----------------------------------------------------------------------------
976 * TclX_KeyedListGet --
977 * Retrieve a key value from a keyed list.
978 *
979 * Parameters:
980 * o interp - Error message will be return in result if there is an error.
981 * o keylPtr - Keyed list object to get key from.
982 * o key - The name of the key to extract. Will recursively process sub-keys
983 * separated by `.'.
984 * o valueObjPtrPtr - If the key is found, a pointer to the key object
985 * is returned here. NULL is returned if the key is not present.
986 * Returns:
987 * o TCL_OK - If the key value was returned.
988 * o TCL_BREAK - If the key was not found.
989 * o TCL_ERROR - If an error occurred.
990 *-----------------------------------------------------------------------------
991 */
992int
993TclX_KeyedListGet(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key, Tcl_Obj **valuePtrPtr)
994{
995 int result = TCL_OK0;
996
997 if (Tcl_ConvertToType(interp, keylPtr, &keyedListType) != TCL_OK0) {
998 result = TCL_ERROR1;
999
1000 } else {
1001 const char *nextSubKey;
1002 const keylIntObj_t *keylIntPtr = (keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
1003 int findIdx;
1004
1005 KEYL_REP_ASSERT(keylIntPtr);
1006
1007 findIdx = FindKeyedListEntry(keylIntPtr, key, NULL((void*)0), &nextSubKey);
1008
1009 /*
1010 * If not found, return status.
1011 */
1012 if (findIdx < 0) {
1013 *valuePtrPtr = NULL((void*)0);
1014 result = TCL_BREAK3;
1015 } else {
1016
1017 /*
1018 * If we are at the last subkey, return the entry, otherwise recurse
1019 * down looking for the entry.
1020 */
1021 if (nextSubKey == NULL((void*)0)) {
1022 *valuePtrPtr = keylIntPtr->entries[findIdx].valuePtr;
1023 } else {
1024 result = TclX_KeyedListGet(interp,
1025 keylIntPtr->entries[findIdx].valuePtr,
1026 nextSubKey,
1027 valuePtrPtr);
1028 }
1029 }
1030 }
1031 return result;
1032}
1033
1034/*-----------------------------------------------------------------------------
1035 * TclX_KeyedListSet --
1036 * Set a key value in keyed list object.
1037 *
1038 * Parameters:
1039 * o interp - Error message will be return in result object.
1040 * o keylPtr - Keyed list object to update.
1041 * o key - The name of the key to extract. Will recursively process
1042 * sub-key separated by `.'.
1043 * o valueObjPtr - The value to set for the key.
1044 * Returns:
1045 * TCL_OK or TCL_ERROR.
1046 *-----------------------------------------------------------------------------
1047 */
1048int
1049TclX_KeyedListSet(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key, Tcl_Obj *valuePtr)
1050{
1051 int result = TCL_OK0;
1052
1053 if (Tcl_ConvertToType(interp, keylPtr, &keyedListType) != TCL_OK0) {
1054 result = TCL_ERROR1;
1055 } else {
1056 const char *nextSubKey;
1057 size_t keyLen;
1058 keylIntObj_t *keylIntPtr = (keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
1059 int findIdx;
1060
1061 KEYL_REP_ASSERT(keylIntPtr);
1062
1063 findIdx = FindKeyedListEntry(keylIntPtr, key,
1064 &keyLen, &nextSubKey);
1065
1066 /*
1067 * If we are at the last subkey, either update or add an
1068 * entry.
1069 */
1070 if (nextSubKey == NULL((void*)0)) {
1071 if (findIdx < 0) {
1072 EnsureKeyedListSpace(keylIntPtr, 1);
1073 findIdx = keylIntPtr->numEntries;
1074 keylIntPtr->numEntries++;
1075 } else {
1076 ns_free(keylIntPtr->entries[findIdx].key);
1077 Tcl_DecrRefCount(keylIntPtr->entries[findIdx].valuePtr)do { Tcl_Obj *_objPtr = (keylIntPtr->entries[findIdx].valuePtr
); if (_objPtr->refCount-- <= 1) { TclFreeObj(_objPtr);
} } while(0)
;
1078 }
1079 keylIntPtr->entries[findIdx].key = (char *)ns_malloc((unsigned)keyLen + 1u);
Duplicate code detected
1080 memcpy(keylIntPtr->entries[findIdx].key, key, keyLen);
1081 keylIntPtr->entries[findIdx].key[keyLen] = '\0';
1082 keylIntPtr->entries[findIdx].valuePtr = valuePtr;
1083 Tcl_IncrRefCount(valuePtr)++(valuePtr)->refCount;
1084 Tcl_InvalidateStringRep(keylPtr);
1085
1086 KEYL_REP_ASSERT(keylIntPtr);
1087 } else {
1088
1089 /*
1090 * If we are not at the last subkey, recurse down, creating
1091 * new entries if necessary. If this level key was not
1092 * found, it means we must build new subtree. Don't insert the
1093 * new tree until we come back without error.
1094 */
1095 if (findIdx >= 0) {
1096
1097 DupSharedKeyListChild(keylIntPtr, findIdx);
1098 result = TclX_KeyedListSet(interp,
1099 keylIntPtr->entries[findIdx].valuePtr,
1100 nextSubKey, valuePtr);
1101 if (result == TCL_OK0) {
1102 Tcl_InvalidateStringRep(keylPtr);
1103 }
1104
1105 KEYL_REP_ASSERT(keylIntPtr);
1106
1107 } else {
1108 Tcl_Obj *newKeylPtr = TclX_NewKeyedListObj();
1109
1110 if (TclX_KeyedListSet(interp, newKeylPtr,
1111 nextSubKey, valuePtr) != TCL_OK0) {
1112 Tcl_DecrRefCount(newKeylPtr)do { Tcl_Obj *_objPtr = (newKeylPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
1113 result = TCL_ERROR1;
1114 } else {
1115 EnsureKeyedListSpace(keylIntPtr, 1);
1116 findIdx = keylIntPtr->numEntries++;
1117 keylIntPtr->entries[findIdx].key = (char *)ns_malloc((unsigned)keyLen + 1u);
Similar code here
1118 memcpy(keylIntPtr->entries[findIdx].key, key, keyLen);
1119 keylIntPtr->entries[findIdx].key[keyLen] = '\0';
1120 keylIntPtr->entries[findIdx].valuePtr = newKeylPtr;
1121 Tcl_IncrRefCount(newKeylPtr)++(newKeylPtr)->refCount;
1122 Tcl_InvalidateStringRep(keylPtr);
1123 }
1124
1125 KEYL_REP_ASSERT(keylIntPtr);
1126 }
1127 }
1128 }
1129 return result;
1130}
1131
1132/*-----------------------------------------------------------------------------
1133 * TclX_KeyedListDelete --
1134 * Delete a key value from keyed list.
1135 *
1136 * Parameters:
1137 * o interp - Error message will be return in result if there is an error.
1138 * o keylPtr - Keyed list object to update.
1139 * o key - The name of the key to extract. Will recursively process
1140 * sub-key separated by `.'.
1141 * Returns:
1142 * o TCL_OK - If the key was deleted.
1143 * o TCL_BREAK - If the key was not found.
1144 * o TCL_ERROR - If an error occurred.
1145 *-----------------------------------------------------------------------------
1146 */
1147int
1148TclX_KeyedListDelete(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key)
1149{
1150 int status;
1151
1152 if (Tcl_ConvertToType(interp, keylPtr, &keyedListType) != TCL_OK0) {
1153 status = TCL_ERROR1;
1154
1155 } else {
1156 int findIdx;
1157 const char *nextSubKey;
1158 keylIntObj_t *keylIntPtr = (keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
1159
1160 findIdx = FindKeyedListEntry(keylIntPtr, key, NULL((void*)0), &nextSubKey);
1161
1162 /*
1163 * If not found, return status.
1164 */
1165 if (findIdx < 0) {
1166 status = TCL_BREAK3;
1167
1168 } else if (nextSubKey == NULL((void*)0)) {
1169 /*
1170 * If we are at the last subkey, delete the entry.
1171 */
1172 DeleteKeyedListEntry(keylIntPtr, findIdx);
1173 Tcl_InvalidateStringRep(keylPtr);
1174 status = TCL_OK0;
1175
1176 } else {
1177 /*
1178 * If we are not at the last subkey, recurse down. If the entry
1179 * is deleted and the sub-keyed list is empty, delete it as well.
1180 * Must invalidate string, as it caches all representations below
1181 * it.
1182 */
1183 DupSharedKeyListChild(keylIntPtr, findIdx);
1184
1185 status = TclX_KeyedListDelete(interp,
1186 keylIntPtr->entries[findIdx].valuePtr,
1187 nextSubKey);
1188 if (status == TCL_OK0) {
1189 const keylIntObj_t *subKeylIntPtr;
1190
1191 subKeylIntPtr = (keylIntObj_t *)
1192 keylIntPtr->entries[findIdx].valuePtr->internalRep.otherValuePtr;
1193 if (subKeylIntPtr->numEntries == 0) {
1194 DeleteKeyedListEntry(keylIntPtr, findIdx);
1195 }
1196 Tcl_InvalidateStringRep(keylPtr);
1197 }
1198 }
1199 KEYL_REP_ASSERT(keylIntPtr);
1200 }
1201
1202 return status;
1203}
1204
1205/*-----------------------------------------------------------------------------
1206 * TclX_KeyedListGetKeys --
1207 * Retrieve a list of keyed list keys.
1208 *
1209 * Parameters:
1210 * o interp - Error message will be return in result if there is an error.
1211 * o keylPtr - Keyed list object to get key from.
1212 * o key - The name of the key to get the sub keys for. NULL or empty
1213 * to retrieve all top level keys.
1214 * o listObjPtrPtr - List object is returned here with key as values.
1215 * Returns:
1216 * o TCL_OK - If the zero or more key where returned.
1217 * o TCL_BREAK - If the key was not found.
1218 * o TCL_ERROR - If an error occurred.
1219 *-----------------------------------------------------------------------------
1220 */
1221int
1222TclX_KeyedListGetKeys(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key, Tcl_Obj **listObjPtrPtr)
1223{
1224 int result = TCL_OK0;
1225
1226 if (Tcl_ConvertToType(interp, keylPtr, &keyedListType) != TCL_OK0) {
1227 result = TCL_ERROR1;
1228
1229 } else {
1230 const keylIntObj_t *keylIntPtr;
1231
1232 keylIntPtr = (keylIntObj_t *) keylPtr->internalRep.otherValuePtr;
1233
1234 /*
1235 * If key is not NULL or empty, then recurse down until we go past
1236 * the end of all of the elements of the key.
1237 */
1238 if ((key != NULL((void*)0)) && (key[0] != '\0')) {
1239 const char *nextSubKey;
1240 int findIdx = FindKeyedListEntry(keylIntPtr, key, NULL((void*)0), &nextSubKey);
1241
1242 if (findIdx < 0) {
1243 assert(keylIntPtr->arraySize >= keylIntPtr->numEntries)((void) (0));
1244 result = TCL_BREAK3;
1245 } else {
1246 assert(keylIntPtr->arraySize >= keylIntPtr->numEntries)((void) (0));
1247 result = TclX_KeyedListGetKeys(interp,
1248 keylIntPtr->entries[findIdx].valuePtr,
1249 nextSubKey,
1250 listObjPtrPtr);
1251 }
1252 } else {
1253 /*
1254 * Reached the end of the full key, return all keys at this level.
1255 */
1256 int idx;
1257 Tcl_Obj *listObjPtr = Tcl_NewListObj(0, NULL((void*)0));
1258
1259 for (idx = 0u; idx < keylIntPtr->numEntries; idx++) {
1260 Tcl_Obj *nameObjPtr = Tcl_NewStringObj(keylIntPtr->entries[idx].key, -1);
1261
1262 if (Tcl_ListObjAppendElement(interp, listObjPtr,
1263 nameObjPtr) != TCL_OK0) {
1264 Tcl_DecrRefCount(nameObjPtr)do { Tcl_Obj *_objPtr = (nameObjPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
1265 Tcl_DecrRefCount(listObjPtr)do { Tcl_Obj *_objPtr = (listObjPtr); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
1266 result = TCL_ERROR1;
1267 break;
1268 }
1269 }
1270 if (result == TCL_OK0) {
1271 *listObjPtrPtr = listObjPtr;
1272 assert(keylIntPtr->arraySize >= keylIntPtr->numEntries)((void) (0));
1273 }
1274 }
1275 }
1276 return result;
1277}
1278
1279/*-----------------------------------------------------------------------------
1280 * Tcl_KeylgetObjCmd --
1281 * Implements the Tcl keylget command:
1282 * keylget listvar ?key? ?retvar | {}?
1283 *-----------------------------------------------------------------------------
1284 */
1285int
1286TclX_KeylgetObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1287{
1288 Tcl_Obj *keylPtr, *valuePtr;
1289 int keyLen, status;
1290
1291 if ((objc < 2) || (objc > 4)) {
1292 status = TclX_WrongArgs(interp, objv[0], "listvar ?key? ?retvar | {}?");
1293
1294 } else if (objc == 2) {
1295 /*
1296 * Handle request for list of keys, use keylkeys command.
1297 */
1298 status = TclX_KeylkeysObjCmd(clientData, interp, objc, objv);
1299
1300 } else {
1301 keylPtr = Tcl_ObjGetVar2(interp, objv[1], NULL((void*)0), TCL_LEAVE_ERR_MSG0x200);
1302 if (keylPtr == NULL((void*)0)) {
1303 status = TCL_ERROR1;
1304
1305 } else {
1306 const char *key;
1307
1308 /*
1309 * Handle retrieving a value for a specified key.
1310 */
1311 key = Tcl_GetStringFromObj(objv[2], &keyLen);
1312 if (ValidateKey(interp, key, keyLen, TRUE1) == TCL_ERROR1) {
1313 status = TCL_ERROR1;
1314 } else {
1315
1316 status = TclX_KeyedListGet(interp, keylPtr, key, &valuePtr);
1317 if (status == TCL_BREAK3) {
1318 /*
1319 * Handle key not found.
1320 */
1321 if (objc == 3) {
1322 Ns_TclPrintfResult(interp, "key \"%s\" not found in keyed list", key);
1323 status = TCL_ERROR1;
1324 } else {
1325 status = TCL_OK0;
1326 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(NS_FALSE)Tcl_NewIntObj((0)!=0));
1327 }
1328
1329 } else if (status == TCL_OK0) {
1330 if (objc == 3) {
1331 /*
1332 * No variable specified, so return value in the result.
1333 */
1334 Tcl_SetObjResult(interp, valuePtr);
1335 } else {
1336
1337 /*
1338 * Variable (or empty variable name) specified.
1339 */
1340 if (!TclX_IsNullObj(objv[3])) {
1341 if (Tcl_SetVar2Ex(interp, Tcl_GetStringFromObj(objv[3], NULL((void*)0)), NULL((void*)0),
1342 valuePtr, TCL_LEAVE_ERR_MSG0x200) == NULL((void*)0)) {
1343 status = TCL_ERROR1;
1344 }
1345 }
1346 if (status == TCL_OK0) {
1347 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(NS_TRUE)Tcl_NewIntObj((1)!=0));
1348 }
1349 }
1350 } else /* (status == TCL_ERROR) */ {
1351 }
1352 }
1353 }
1354 }
1355 return status;
1356}
1357
1358/*-----------------------------------------------------------------------------
1359 * Tcl_KeylsetObjCmd --
1360 * Implements the Tcl keylset command:
1361 * keylset listvar key value ?key value...?
1362 *-----------------------------------------------------------------------------
1363 */
1364int
1365TclX_KeylsetObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1366{
1367 int result = TCL_OK0;
1368
1369 if ((objc < 4) || ((objc % 2) != 0)) {
1370 result = TclX_WrongArgs(interp, objv[0],
1371 "listvar key value ?key value...?");
1372 } else {
1373 Tcl_Obj *keylVarPtr, *newVarObj;
1374 int idx, keyLen;
1375
1376 /*
1377 * Get the variable that we are going to update. If the var
1378 * doesn't exist, create it. If it is shared by more than
1379 * being a variable, duplicated it.
1380 */
1381 keylVarPtr = Tcl_ObjGetVar2(interp, objv[1], NULL((void*)0), 0);
1382 if (keylVarPtr == NULL((void*)0)) {
1383 newVarObj = keylVarPtr = TclX_NewKeyedListObj();
1384 Tcl_IncrRefCount(newVarObj)++(newVarObj)->refCount;
1385 } else if (Tcl_IsShared(keylVarPtr)((keylVarPtr)->refCount > 1)) {
1386 newVarObj = keylVarPtr = Tcl_DuplicateObj(keylVarPtr);
1387 Tcl_IncrRefCount(newVarObj)++(newVarObj)->refCount;
1388 } else {
1389 newVarObj = NULL((void*)0);
1390 }
1391
1392 for (idx = 2; idx < objc; idx += 2) {
1393 const char *key = Tcl_GetStringFromObj(objv[idx], &keyLen);
1394
1395 result = ValidateKey(interp, key, keyLen, TRUE1);
1396 if (result == TCL_ERROR1) {
1397 break;
1398 }
1399 result = TclX_KeyedListSet(interp, keylVarPtr, key, objv[idx+1]);
1400 if (result == TCL_ERROR1) {
1401 break;
1402 }
1403 }
1404
1405 if ((result == TCL_OK0)
1406 && (Tcl_ObjSetVar2(interp, objv[1], NULL((void*)0), keylVarPtr,
1407 TCL_LEAVE_ERR_MSG0x200) == NULL((void*)0))) {
1408 result = TCL_ERROR1;
1409 }
1410
1411 if (newVarObj != NULL((void*)0)) {
1412 Tcl_DecrRefCount(newVarObj)do { Tcl_Obj *_objPtr = (newVarObj); if (_objPtr->refCount
-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
1413 }
1414 }
1415 return result;
1416}
1417
1418/*-----------------------------------------------------------------------------
1419 * Tcl_KeyldelObjCmd --
1420 * Implements the Tcl keyldel command:
1421 * keyldel listvar key ?key ...?
1422 *----------------------------------------------------------------------------
1423 */
1424int
1425TclX_KeyldelObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1426{
1427 int result = TCL_OK0;
1428
1429 if (objc < 3) {
1430 result = TclX_WrongArgs(interp, objv[0], "listvar key ?key ...?");
1431
1432 } else {
1433 Tcl_Obj *keylVarPtr;
1434
1435 /*
1436 * Get the variable that we are going to update. If it is shared
1437 * by more than being a variable, duplicated it.
1438 */
1439 keylVarPtr = Tcl_ObjGetVar2(interp, objv[1], NULL((void*)0), TCL_LEAVE_ERR_MSG0x200);
1440 if (keylVarPtr == NULL((void*)0)) {
1441 result = TCL_ERROR1;
1442
1443 } else {
1444 Tcl_Obj *keylPtr;
1445
1446 if (Tcl_IsShared(keylVarPtr)((keylVarPtr)->refCount > 1)) {
1447 keylPtr = Tcl_DuplicateObj(keylVarPtr);
1448 keylVarPtr = Tcl_ObjSetVar2(interp, objv[1], NULL((void*)0), keylPtr,
1449 TCL_LEAVE_ERR_MSG0x200);
1450 if (keylVarPtr == NULL((void*)0)) {
1451 Tcl_DecrRefCount(keylPtr)do { Tcl_Obj *_objPtr = (keylPtr); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
1452 result = TCL_ERROR1;
1453 } else if (keylVarPtr != keylPtr) {
1454 Tcl_DecrRefCount(keylPtr)do { Tcl_Obj *_objPtr = (keylPtr); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
1455 }
1456 }
1457 if (result == TCL_OK0) {
1458 int idx;
1459
1460 keylPtr = keylVarPtr;
1461
1462 for (idx = 2; idx < objc; idx++) {
1463 int keyLen;
1464 const char *key = Tcl_GetStringFromObj(objv[idx], &keyLen);
1465
1466 if (ValidateKey(interp, key, keyLen, TRUE1) == TCL_ERROR1) {
1467 result = TCL_ERROR1;
1468 } else {
1469 result = TclX_KeyedListDelete(interp, keylPtr, key);
1470
1471 if (result == TCL_BREAK3) {
1472 Ns_TclPrintfResult(interp, "key not found: \"%s\"", key);
1473 result = TCL_ERROR1;
1474 }
1475 }
1476 }
1477 }
1478 }
1479 }
1480 return result;
1481}
1482
1483/*-----------------------------------------------------------------------------
1484 * Tcl_KeylkeysObjCmd --
1485 * Implements the Tcl keylkeys command:
1486 * keylkeys listvar ?key?
1487 *-----------------------------------------------------------------------------
1488 */
1489int
1490TclX_KeylkeysObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1491{
1492 int result;
1493
1494 if ((objc < 2) || (objc > 3)) {
1495 result = TclX_WrongArgs(interp, objv[0], "listvar ?key?");
1496 } else {
1497 const char *varName = Tcl_GetStringFromObj(objv[1], NULL((void*)0));
1498 Tcl_Obj *keylPtr = Tcl_GetVar2Ex(interp, varName, NULL((void*)0), TCL_LEAVE_ERR_MSG0x200);
1499
1500 if (keylPtr == NULL((void*)0)) {
1501 result = TCL_ERROR1;
1502
1503 } else {
1504 const char *key;
1505 Tcl_Obj *listObjPtr = NULL((void*)0);
1506
1507 /*
1508 * If "key" argument is not specified, then objv[2] is NULL or
1509 * empty, meaning get top level keys.
1510 */
1511 if (objc < 3) {
1512 key = NULL((void*)0);
1513 result = TCL_OK0;
1514 } else {
1515 int keyLen;
1516
1517 key = Tcl_GetStringFromObj(objv[2], &keyLen);
1518 result = ValidateKey(interp, key, keyLen, TRUE1);
1519 }
1520
1521 if (result == TCL_OK0) {
1522 result = TclX_KeyedListGetKeys(interp, keylPtr, key, &listObjPtr);
1523 if (result == TCL_BREAK3) {
1524 Ns_TclPrintfResult(interp, "key not found: \"%s\"", key != NULL((void*)0) ? key : "");
1525 result = TCL_ERROR1;
1526 }
1527 }
1528
1529 if (result == TCL_OK0) {
1530 Tcl_SetObjResult(interp, listObjPtr);
1531 }
1532 }
1533 }
1534 return result;
1535}
1536
1537/*
1538 * Local Variables:
1539 * mode: c
1540 * c-basic-offset: 4
1541 * fill-column: 70
1542 * indent-tabs-mode: nil
1543 * End:
1544 */