2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
9 * You may distribute under the terms of the GNU General Public License as
10 * specified in the README file that comes with the CVS source distribution.
12 * Polk's hash list manager. So cool.
17 /* Global caches. The idea is that we maintain a linked list of "free"d
18 nodes or lists, and get new items from there. It has been suggested
19 to use an obstack instead, but off the top of my head, I'm not sure
20 that would gain enough to be worth worrying about. */
21 static List
*listcache
= NULL
;
22 static Node
*nodecache
= NULL
;
24 static void freenode_mem (Node
* p
);
28 hashp (const char *key
)
37 unsigned int c
= *key
++;
38 /* The FOLD_FN_CHAR is so that findnode_fn works. */
39 h
= (h
<< 4) + FOLD_FN_CHAR (c
);
40 if ((g
= h
& 0xf0000000) != 0)
41 h
= (h
^ (g
>> 24)) ^ g
;
50 * create a new list (or get an old one from the cache)
59 if (listcache
!= NULL
)
61 /* get a list from the cache and clear it */
63 listcache
= listcache
->next
;
65 for (i
= 0; i
< HASHSIZE
; i
++)
66 list
->hasharray
[i
] = NULL
;
70 /* make a new list from scratch */
71 list
= xmalloc (sizeof (List
));
72 memset (list
, 0, sizeof (List
));
76 node
->next
= node
->prev
= node
;
84 * Free up a list. For accessing globals which might be accessed via interrupt
85 * handlers, it can be assumed that the first action of this function will be
86 * to set the *listp to NULL.
89 dellist (List
**listp
)
103 /* free each node in the list (except header) */
107 /* free any list-private data, without freeing the actual header */
110 /* free up the header nodes for hash lists (if any) */
111 for (i
= 0; i
< HASHSIZE
; i
++)
113 if ((p
= tmp
->hasharray
[i
]) != NULL
)
115 /* put the nodes into the cache */
117 p
->type
= NT_UNKNOWN
;
121 /* If NOCACHE is defined we turn off the cache. This can make
122 it easier to tools to determine where items were allocated
123 and freed, for tracking down memory leaks and the like. */
129 /* put it on the cache */
131 tmp
->next
= listcache
;
142 * remove a node from it's list (maybe hash list too)
149 /* take it out of the list */
150 p
->next
->prev
= p
->prev
;
151 p
->prev
->next
= p
->next
;
153 /* if it was hashed, remove it from there too */
156 p
->hashnext
->hashprev
= p
->hashprev
;
157 p
->hashprev
->hashnext
= p
->hashnext
;
164 mergelists (List
*dest
, List
**src
)
169 for (p
= head
->next
; p
!= head
; p
= n
)
181 * get a new list node
188 if (nodecache
!= NULL
)
190 /* get one from the cache */
197 p
= xmalloc (sizeof (Node
));
200 /* always make it clean */
201 memset (p
, 0, sizeof (Node
));
202 p
->type
= NT_UNKNOWN
;
210 * remove a node from it's list (maybe hash list too) and free it
218 /* free up the storage */
225 * free up the storage associated with a node
228 freenode_mem (Node
*p
)
230 if (p
->delproc
!= NULL
)
231 p
->delproc (p
); /* call the specified delproc */
234 if (p
->data
!= NULL
) /* otherwise free() it if necessary */
237 if (p
->key
!= NULL
) /* free the key if necessary */
240 /* to be safe, re-initialize these */
241 p
->key
= p
->data
= NULL
;
248 * free up the storage associated with a node and recycle it
253 /* first free the memory */
256 /* then put it in the cache */
258 p
->type
= NT_UNKNOWN
;
269 * Link item P into list LIST before item MARKER. If P->KEY is non-NULL and
270 * that key is already in the hash table, return -1 without modifying any
273 * return 0 on success
276 insert_before (List
*list
, Node
*marker
, Node
*p
)
278 if (p
->key
!= NULL
) /* hash it too? */
283 hashval
= hashp (p
->key
);
284 if (list
->hasharray
[hashval
] == NULL
) /* make a header for list? */
288 list
->hasharray
[hashval
] = q
->hashnext
= q
->hashprev
= q
;
291 /* put it into the hash list if it's not already there */
292 for (q
= list
->hasharray
[hashval
]->hashnext
;
293 q
!= list
->hasharray
[hashval
]; q
= q
->hashnext
)
295 if (strcmp (p
->key
, q
->key
) == 0)
298 q
= list
->hasharray
[hashval
];
299 p
->hashprev
= q
->hashprev
;
301 p
->hashprev
->hashnext
= p
;
306 p
->prev
= marker
->prev
;
307 marker
->prev
->next
= p
;
316 * insert item p at end of list "list" (maybe hash it too) if hashing and it
317 * already exists, return -1 and don't actually put it in the list
319 * return 0 on success
322 addnode (List
*list
, Node
*p
)
324 return insert_before (list
, list
->list
, p
);
330 * Like addnode, but insert p at the front of `list'. This bogosity is
331 * necessary to preserve last-to-first output order for some RCS functions.
334 addnode_at_front (List
*list
, Node
*p
)
336 return insert_before (list
, list
->list
->next
, p
);
341 /* Look up an entry in hash list table and return a pointer to the
342 * node. Return NULL if not found or if list is NULL. Abort with a fatal
346 findnode (List
*list
, const char *key
)
353 assert (key
!= NULL
);
355 head
= list
->hasharray
[hashp (key
)];
360 for (p
= head
->hashnext
; p
!= head
; p
= p
->hashnext
)
361 if (strcmp (p
->key
, key
) == 0)
369 * Like findnode, but for a filename.
372 findnode_fn (List
*list
, const char *key
)
376 /* This probably should be "assert (list != NULL)" (or if not we
377 should document the current behavior), but only if we check all
378 the callers to see if any are relying on this behavior. */
382 assert (key
!= NULL
);
384 head
= list
->hasharray
[hashp (key
)];
388 for (p
= head
->hashnext
; p
!= head
; p
= p
->hashnext
)
389 if (fncmp (p
->key
, key
) == 0)
397 * walk a list with a specific proc
400 walklist (List
*list
, int (*proc
) (Node
*, void *), void *closure
)
405 #ifdef HAVE_PRINTF_PTR
406 TRACE (TRACE_FLOW
, "walklist ( list=%p, proc=%p, closure=%p )",
407 (void *)list
, (void *)proc
, (void *)closure
);
409 TRACE (TRACE_FLOW
, "walklist ( list=%lx, proc=%lx, closure=%lx )",
410 (unsigned long)list
,(unsigned long) proc
,
411 (unsigned long)closure
);
418 for (p
= head
->next
; p
!= head
; p
= p
->next
)
419 err
+= proc (p
, closure
);
426 list_isempty (List
*list
)
428 return list
== NULL
|| list
->list
->next
== list
->list
;
433 static int (*client_comp
) (const Node
*, const Node
*);
436 qsort_comp (const void *elem1
, const void *elem2
)
438 Node
**node1
= (Node
**) elem1
;
439 Node
**node2
= (Node
**) elem2
;
440 return client_comp (*node1
, *node2
);
446 * sort the elements of a list (in place)
449 sortlist (List
*list
, int (*comp
) (const Node
*, const Node
*))
451 Node
*head
, *remain
, *p
, **array
;
457 /* save the old first element of the list */
461 /* count the number of nodes in the list */
463 for (p
= remain
; p
!= head
; p
= p
->next
)
466 /* allocate an array of nodes and populate it */
467 array
= xnmalloc (n
, sizeof (Node
*));
469 for (p
= remain
; p
!= head
; p
= p
->next
)
472 /* sort the array of nodes */
474 qsort (array
, n
, sizeof(Node
*), qsort_comp
);
476 /* rebuild the list from beginning to end */
477 head
->next
= head
->prev
= head
;
478 for (i
= 0; i
< n
; i
++)
482 p
->prev
= head
->prev
;
487 /* release the array of nodes */
494 * compare two files list node (for sort)
497 fsortcmp (const Node
*p
, const Node
*q
)
499 return strcmp (p
->key
, q
->key
);
504 /* Debugging functions. Quite useful to call from within gdb. */
508 nodetypestring (Ntype type
)
511 case NT_UNKNOWN
: return "UNKNOWN";
512 case HEADER
: return "HEADER";
513 case ENTRIES
: return "ENTRIES";
514 case FILES
: return "FILES";
515 case LIST
: return "LIST";
516 case RCSNODE
: return "RCSNODE";
517 case RCSVERS
: return "RCSVERS";
518 case DIRS
: return "DIRS";
519 case UPDATE
: return "UPDATE";
520 case LOCK
: return "LOCK";
521 case NDBMNODE
: return "NDBMNODE";
522 case FILEATTR
: return "FILEATTR";
523 case VARIABLE
: return "VARIABLE";
524 case RCSFIELD
: return "RCSFIELD";
525 case RCSCMPFLD
: return "RCSCMPFLD";
534 printnode (Node
*node
, void *closure
)
538 (void) printf("NULL node.\n");
542 #ifdef HAVE_PRINTF_PTR
543 (void) printf("Node at %p: type = %s, key = %p = \"%s\", data = %p, next = %p, prev = %p\n",
544 (void *) node
, nodetypestring(node
->type
),
545 (void *) node
->key
, node
->key
, node
->data
,
546 (void *) node
->next
, (void *) node
->prev
);
548 (void) printf("Node at 0x%lx: type = %s, key = 0x%lx = \"%s\", data = 0x%lx, next = 0x%lx, prev = 0x%lx\n",
549 (unsigned long) node
, nodetypestring(node
->type
),
550 (unsigned long) node
->key
, node
->key
, (unsigned long) node
->data
,
551 (unsigned long) node
->next
, (unsigned long) node
->prev
);
559 /* This is global, not static, so that its name is unique and to avoid
560 compiler warnings about it not being used. But it is not used by CVS;
561 it exists so one can call it from a debugger. */
564 printlist (List
*list
)
568 (void) printf("NULL list.\n");
572 #ifdef HAVE_PRINTF_PTR
573 (void) printf("List at %p: list = %p, HASHSIZE = %d, next = %p\n",
574 (void *) list
, (void *) list
->list
, HASHSIZE
, (void *) list
->next
);
576 (void) printf("List at 0x%lx: list = 0x%lx, HASHSIZE = %d, next = 0x%lx\n",
577 (unsigned long) list
, (unsigned long) list
->list
, HASHSIZE
,
578 (unsigned long) list
->next
);
581 (void) walklist(list
, printnode
, NULL
);