File: | d/tclxkeylist.c |
Warning: | line 1079, column 13 Duplicate code detected |
Note: | line 1117, column 21 Similar code here |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
27 | static int TclX_WrongArgs(Tcl_Interp *interp, Tcl_Obj *commandNameObj, const char *msg); |
28 | static 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 | */ |
51 | static 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 | */ |
57 | void 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 | */ |
74 | static int |
75 | TclX_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 | */ |
97 | static bool_Bool |
98 | TclX_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 | |
126 | Tcl_Obj* |
127 | TclX_NewKeyedListObj(void); |
128 | |
129 | int |
130 | TclX_KeyedListGet(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key, |
131 | Tcl_Obj **valuePtrPtr); |
132 | int |
133 | TclX_KeyedListSet(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key, |
134 | Tcl_Obj *valuePtr); |
135 | int |
136 | TclX_KeyedListDelete(Tcl_Interp *interp, Tcl_Obj *keylPtr, const char *key); |
137 | |
138 | int |
139 | TclX_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 | |
176 | int |
177 | Tcl_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 | |
262 | int |
263 | Tcl_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 | |
315 | char * |
316 | Tcl_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 | |
362 | char * |
363 | Tcl_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 | */ |
402 | typedef struct { |
403 | char *key; |
404 | Tcl_Obj *valuePtr; |
405 | } keylEntry_t; |
406 | |
407 | /* |
408 | * Internal representation of a keyed list object. |
409 | */ |
410 | typedef 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 | */ |
439 | static void DupSharedKeyListChild(const keylIntObj_t *keylIntPtr, int idx) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); |
440 | |
441 | #ifdef TCLX_DEBUG |
442 | static void |
443 | ValidateKeyedList(keylIntObj_t *keylIntPtr); |
444 | #endif |
445 | |
446 | static int |
447 | ValidateKey(Tcl_Interp *interp, const char *key, int keyLen, int isPath); |
448 | |
449 | static keylIntObj_t * |
450 | AllocKeyedListIntRep(void); |
451 | |
452 | static void |
453 | FreeKeyedListData(keylIntObj_t *keylIntPtr); |
454 | |
455 | static void |
456 | EnsureKeyedListSpace(keylIntObj_t *keylIntPtr, |
457 | int newNumEntries); |
458 | |
459 | static void |
460 | DeleteKeyedListEntry(keylIntObj_t *keylIntPtr, |
461 | int entryIdx); |
462 | |
463 | static int |
464 | FindKeyedListEntry(const keylIntObj_t *keylIntPtr, |
465 | const char *key, |
466 | size_t *keyLenPtr, |
467 | const char **nextSubKeyPtr); |
468 | |
469 | static int |
470 | ObjToKeyedListEntry(Tcl_Interp *interp, |
471 | Tcl_Obj *objPtr, |
472 | keylEntry_t *entryPtr); |
473 | |
474 | |
475 | static Tcl_FreeInternalRepProc FreeKeyedListInternalRep; |
476 | static Tcl_DupInternalRepProc DupKeyedListInternalRep; |
477 | static Tcl_UpdateStringProc UpdateStringOfKeyedList; |
478 | static Tcl_SetFromAnyProc SetKeyedListFromAny; |
479 | |
480 | /* |
481 | * Type definition. |
482 | */ |
483 | static 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 | */ |
501 | static void |
502 | DupSharedKeyListChild(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 |
522 | static void |
523 | ValidateKeyedList(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 | */ |
561 | static int |
562 | ValidateKey(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 | */ |
599 | static keylIntObj_t * |
600 | AllocKeyedListIntRep(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 | */ |
621 | static void |
622 | FreeKeyedListData(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 | */ |
647 | static void |
648 | EnsureKeyedListSpace(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 | */ |
678 | static void |
679 | DeleteKeyedListEntry(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 | */ |
709 | static int |
710 | FindKeyedListEntry(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 | */ |
762 | static int |
763 | ObjToKeyedListEntry(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 | */ |
805 | static void |
806 | FreeKeyedListInternalRep(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 | */ |
820 | static void |
821 | DupKeyedListInternalRep(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 | */ |
862 | static int |
863 | SetKeyedListFromAny(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 | */ |
911 | static void |
912 | UpdateStringOfKeyedList(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 | */ |
964 | Tcl_Obj * |
965 | TclX_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 | */ |
992 | int |
993 | TclX_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 | */ |
1048 | int |
1049 | TclX_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 | */ |
1147 | int |
1148 | TclX_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 | */ |
1221 | int |
1222 | TclX_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 | */ |
1285 | int |
1286 | TclX_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 | */ |
1364 | int |
1365 | TclX_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 | */ |
1424 | int |
1425 | TclX_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 | */ |
1489 | int |
1490 | TclX_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 | */ |