1/* 2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3 * 4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5 * and others. 6 * 7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8 * 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. 11 * 12 * Polk's hash list manager. So cool. 13 */ 14 15#include "cvs.h" 16#include <assert.h> 17 18/* Global caches. The idea is that we maintain a linked list of "free"d 19 nodes or lists, and get new items from there. It has been suggested 20 to use an obstack instead, but off the top of my head, I'm not sure 21 that would gain enough to be worth worrying about. */ 22static List *listcache = NULL; 23static Node *nodecache = NULL; 24 25static void freenode_mem PROTO((Node * p)); 26 27/* hash function */ 28static int 29hashp (key) 30 const char *key; 31{ 32 unsigned int h = 0; 33 unsigned int g; 34 35 assert(key != NULL); 36 37 while (*key != 0) 38 { 39 unsigned int c = *key++; 40 /* The FOLD_FN_CHAR is so that findnode_fn works. */ 41 h = (h << 4) + FOLD_FN_CHAR (c); 42 if ((g = h & 0xf0000000) != 0) 43 h = (h ^ (g >> 24)) ^ g; 44 } 45 46 return (h % HASHSIZE); 47} 48 49/* 50 * create a new list (or get an old one from the cache) 51 */ 52List * 53getlist () 54{ 55 int i; 56 List *list; 57 Node *node; 58 59 if (listcache != NULL) 60 { 61 /* get a list from the cache and clear it */ 62 list = listcache; 63 listcache = listcache->next; 64 list->next = (List *) NULL; 65 for (i = 0; i < HASHSIZE; i++) 66 list->hasharray[i] = (Node *) NULL; 67 } 68 else 69 { 70 /* make a new list from scratch */ 71 list = (List *) xmalloc (sizeof (List)); 72 memset ((char *) list, 0, sizeof (List)); 73 node = getnode (); 74 list->list = node; 75 node->type = HEADER; 76 node->next = node->prev = node; 77 } 78 return (list); 79} 80 81/* 82 * free up a list 83 */ 84void 85dellist (listp) 86 List **listp; 87{ 88 int i; 89 Node *p; 90 91 if (*listp == (List *) NULL) 92 return; 93 94 p = (*listp)->list; 95 96 /* free each node in the list (except header) */ 97 while (p->next != p) 98 delnode (p->next); 99 100 /* free any list-private data, without freeing the actual header */ 101 freenode_mem (p); 102 103 /* free up the header nodes for hash lists (if any) */ 104 for (i = 0; i < HASHSIZE; i++) 105 { 106 if ((p = (*listp)->hasharray[i]) != (Node *) NULL) 107 { 108 /* put the nodes into the cache */ 109#ifndef NOCACHE 110 p->type = NT_UNKNOWN; 111 p->next = nodecache; 112 nodecache = p; 113#else 114 /* If NOCACHE is defined we turn off the cache. This can make 115 it easier to tools to determine where items were allocated 116 and freed, for tracking down memory leaks and the like. */ 117 free (p); 118#endif 119 } 120 } 121 122 /* put it on the cache */ 123#ifndef NOCACHE 124 (*listp)->next = listcache; 125 listcache = *listp; 126#else 127 free ((*listp)->list); 128 free (*listp); 129#endif 130 *listp = (List *) NULL; 131} 132 133/* 134 * get a new list node 135 */ 136Node * 137getnode () 138{ 139 Node *p; 140 141 if (nodecache != (Node *) NULL) 142 { 143 /* get one from the cache */ 144 p = nodecache; 145 nodecache = p->next; 146 } 147 else 148 { 149 /* make a new one */ 150 p = (Node *) xmalloc (sizeof (Node)); 151 } 152 153 /* always make it clean */ 154 memset ((char *) p, 0, sizeof (Node)); 155 p->type = NT_UNKNOWN; 156 157 return (p); 158} 159 160/* 161 * remove a node from it's list (maybe hash list too) and free it 162 */ 163void 164delnode (p) 165 Node *p; 166{ 167 if (p == (Node *) NULL) 168 return; 169 170 /* take it out of the list */ 171 p->next->prev = p->prev; 172 p->prev->next = p->next; 173 174 /* if it was hashed, remove it from there too */ 175 if (p->hashnext != (Node *) NULL) 176 { 177 p->hashnext->hashprev = p->hashprev; 178 p->hashprev->hashnext = p->hashnext; 179 } 180 181 /* free up the storage */ 182 freenode (p); 183} 184 185/* 186 * free up the storage associated with a node 187 */ 188static void 189freenode_mem (p) 190 Node *p; 191{ 192 if (p->delproc != (void (*) ()) NULL) 193 p->delproc (p); /* call the specified delproc */ 194 else 195 { 196 if (p->data != NULL) /* otherwise free() it if necessary */ 197 free (p->data); 198 } 199 if (p->key != NULL) /* free the key if necessary */ 200 free (p->key); 201 202 /* to be safe, re-initialize these */ 203 p->key = p->data = NULL; 204 p->delproc = (void (*) ()) NULL; 205} 206 207/* 208 * free up the storage associated with a node and recycle it 209 */ 210void 211freenode (p) 212 Node *p; 213{ 214 /* first free the memory */ 215 freenode_mem (p); 216 217 /* then put it in the cache */ 218#ifndef NOCACHE 219 p->type = NT_UNKNOWN; 220 p->next = nodecache; 221 nodecache = p; 222#else 223 free (p); 224#endif 225} 226 227/* 228 * Link item P into list LIST before item MARKER. If P->KEY is non-NULL and 229 * that key is already in the hash table, return -1 without modifying any 230 * parameter. 231 * 232 * return 0 on success 233 */ 234int 235insert_before (list, marker, p) 236 List *list; 237 Node *marker; 238 Node *p; 239{ 240 if (p->key != NULL) /* hash it too? */ 241 { 242 int hashval; 243 Node *q; 244 245 hashval = hashp (p->key); 246 if (list->hasharray[hashval] == NULL) /* make a header for list? */ 247 { 248 q = getnode (); 249 q->type = HEADER; 250 list->hasharray[hashval] = q->hashnext = q->hashprev = q; 251 } 252 253 /* put it into the hash list if it's not already there */ 254 for (q = list->hasharray[hashval]->hashnext; 255 q != list->hasharray[hashval]; q = q->hashnext) 256 { 257 if (strcmp (p->key, q->key) == 0) 258 return (-1); 259 } 260 q = list->hasharray[hashval]; 261 p->hashprev = q->hashprev; 262 p->hashnext = q; 263 p->hashprev->hashnext = p; 264 q->hashprev = p; 265 } 266 267 p->next = marker; 268 p->prev = marker->prev; 269 marker->prev->next = p; 270 marker->prev = p; 271 272 return (0); 273} 274 275/* 276 * insert item p at end of list "list" (maybe hash it too) if hashing and it 277 * already exists, return -1 and don't actually put it in the list 278 * 279 * return 0 on success 280 */ 281int 282addnode (list, p) 283 List *list; 284 Node *p; 285{ 286 return insert_before (list, list->list, p); 287} 288 289/* 290 * Like addnode, but insert p at the front of `list'. This bogosity is 291 * necessary to preserve last-to-first output order for some RCS functions. 292 */ 293int 294addnode_at_front (list, p) 295 List *list; 296 Node *p; 297{ 298 return insert_before (list, list->list->next, p); 299} 300 301/* Look up an entry in hash list table and return a pointer to the 302 node. Return NULL if not found. Abort with a fatal error for 303 errors. */ 304Node * 305findnode (list, key) 306 List *list; 307 const char *key; 308{ 309 Node *head, *p; 310 311 /* This probably should be "assert (list != NULL)" (or if not we 312 should document the current behavior), but only if we check all 313 the callers to see if any are relying on this behavior. */ 314 if ((list == (List *) NULL)) 315 return ((Node *) NULL); 316 317 assert (key != NULL); 318 319 head = list->hasharray[hashp (key)]; 320 if (head == (Node *) NULL) 321 /* Not found. */ 322 return ((Node *) NULL); 323 324 for (p = head->hashnext; p != head; p = p->hashnext) 325 if (strcmp (p->key, key) == 0) 326 return (p); 327 return ((Node *) NULL); 328} 329 330/* 331 * Like findnode, but for a filename. 332 */ 333Node * 334findnode_fn (list, key) 335 List *list; 336 const char *key; 337{ 338 Node *head, *p; 339 340 /* This probably should be "assert (list != NULL)" (or if not we 341 should document the current behavior), but only if we check all 342 the callers to see if any are relying on this behavior. */ 343 if (list == (List *) NULL) 344 return ((Node *) NULL); 345 346 assert (key != NULL); 347 348 head = list->hasharray[hashp (key)]; 349 if (head == (Node *) NULL) 350 return ((Node *) NULL); 351 352 for (p = head->hashnext; p != head; p = p->hashnext) 353 if (fncmp (p->key, key) == 0) 354 return (p); 355 return ((Node *) NULL); 356} 357 358/* 359 * walk a list with a specific proc 360 */ 361int 362walklist (list, proc, closure) 363 List *list; 364 int (*proc) PROTO ((Node *, void *)); 365 void *closure; 366{ 367 Node *head, *p; 368 int err = 0; 369 370 if (list == NULL) 371 return (0); 372 373 head = list->list; 374 for (p = head->next; p != head; p = p->next) 375 err += proc (p, closure); 376 return (err); 377} 378 379int 380list_isempty (list) 381 List *list; 382{ 383 return list == NULL || list->list->next == list->list; 384} 385 386static int (*client_comp) PROTO ((const Node *, const Node *)); 387static int qsort_comp PROTO ((const void *, const void *)); 388 389static int 390qsort_comp (elem1, elem2) 391 const void *elem1; 392 const void *elem2; 393{ 394 Node **node1 = (Node **) elem1; 395 Node **node2 = (Node **) elem2; 396 return client_comp (*node1, *node2); 397} 398 399/* 400 * sort the elements of a list (in place) 401 */ 402void 403sortlist (list, comp) 404 List *list; 405 int (*comp) PROTO ((const Node *, const Node *)); 406{ 407 Node *head, *remain, *p, **array; 408 int i, n; 409 410 if (list == NULL) 411 return; 412 413 /* save the old first element of the list */ 414 head = list->list; 415 remain = head->next; 416 417 /* count the number of nodes in the list */ 418 n = 0; 419 for (p = remain; p != head; p = p->next) 420 n++; 421 422 /* allocate an array of nodes and populate it */ 423 array = (Node **) xmalloc (sizeof(Node *) * n); 424 i = 0; 425 for (p = remain; p != head; p = p->next) 426 array[i++] = p; 427 428 /* sort the array of nodes */ 429 client_comp = comp; 430 qsort (array, n, sizeof(Node *), qsort_comp); 431 432 /* rebuild the list from beginning to end */ 433 head->next = head->prev = head; 434 for (i = 0; i < n; i++) 435 { 436 p = array[i]; 437 p->next = head; 438 p->prev = head->prev; 439 p->prev->next = p; 440 head->prev = p; 441 } 442 443 /* release the array of nodes */ 444 free (array); 445} 446 447/* 448 * compare two files list node (for sort) 449 */ 450int 451fsortcmp (p, q) 452 const Node *p; 453 const Node *q; 454{ 455 return (strcmp (p->key, q->key)); 456} 457 458/* Debugging functions. Quite useful to call from within gdb. */ 459 460static char *nodetypestring PROTO ((Ntype)); 461 462static char * 463nodetypestring (type) 464 Ntype type; 465{ 466 switch (type) { 467 case NT_UNKNOWN: return("UNKNOWN"); 468 case HEADER: return("HEADER"); 469 case ENTRIES: return("ENTRIES"); 470 case FILES: return("FILES"); 471 case LIST: return("LIST"); 472 case RCSNODE: return("RCSNODE"); 473 case RCSVERS: return("RCSVERS"); 474 case DIRS: return("DIRS"); 475 case UPDATE: return("UPDATE"); 476 case LOCK: return("LOCK"); 477 case NDBMNODE: return("NDBMNODE"); 478 case FILEATTR: return("FILEATTR"); 479 case VARIABLE: return("VARIABLE"); 480 case RCSFIELD: return("RCSFIELD"); 481 case RCSCMPFLD: return("RCSCMPFLD"); 482 } 483 484 return("<trash>"); 485} 486 487static int printnode PROTO ((Node *, void *)); 488static int 489printnode (node, closure) 490 Node *node; 491 void *closure; 492{ 493 if (node == NULL) 494 { 495 (void) printf("NULL node.\n"); 496 return(0); 497 } 498 499 (void) printf("Node at %p: type = %s, key = %p = \"%s\", data = %p, next = %p, prev = %p\n", 500 (void *)node, nodetypestring(node->type), 501 (void *)node->key, node->key, node->data, 502 (void *)node->next, (void *)node->prev); 503 504 return(0); 505} 506 507/* This is global, not static, so that its name is unique and to avoid 508 compiler warnings about it not being used. But it is not used by CVS; 509 it exists so one can call it from a debugger. */ 510void printlist PROTO ((List *)); 511 512void 513printlist (list) 514 List *list; 515{ 516 if (list == NULL) 517 { 518 (void) printf("NULL list.\n"); 519 return; 520 } 521 522 (void) printf("List at %p: list = %p, HASHSIZE = %d, next = %p\n", 523 (void *)list, (void *)list->list, HASHSIZE, (void *)list->next); 524 525 (void) walklist(list, printnode, NULL); 526 527 return; 528} 529