Array_item *add_to_array (Array *array, Array_item *item) { int count; int location = 0; Array_item *ret = (void *) 0 ; u_32int_t mask; if (array->hash == HASH_INSENSITIVE) item->hash = ci_alist_hash(item->name, &mask); else item->hash = cs_alist_hash(item->name, &mask); check_array_size(array); if (array->max) { find_array_item(array, item->name, &count, &location); if (count < 0) { ret = ((Array_item *) (( array ) -> list [ ( location ) ])) ; array->max--; } else move_array_items(array, location, array->max, 1); } array->list[location] = item; array->max++; return ret; }
/* * Returns an entry that has been displaced, if any. */ Array_item *BX_add_to_array (Array *array, Array_item *item) { int count; int location = 0; Array_item *ret = NULL; u_32int_t mask; /* Dummy var */ if (array->hash == HASH_INSENSITIVE) item->hash = ci_alist_hash(item->name, &mask); else item->hash = cs_alist_hash(item->name, &mask); check_array_size(array); if (array->max) { find_array_item(array, item->name, &count, &location); if (count < 0) { ret = ARRAY_ITEM(array, location); array->max--; } else move_array_items(array, location, array->max, 1); } array->list[location] = item; array->max++; return ret; }
/* * This is just a generalization of the old function ``find_command'' * * You give it an alist_item and a name, and it gives you the number of * items that are completed by that name, and where you could find/put that * item in the list. It returns the alist_item most appropriate. * * If ``cnt'' is less than -1, then there was one exact match and one or * more ambiguous matches in addition. The exact match's location * is put into ``loc'' and its entry is returned. The ambigous matches * are (of course) immediately subsequent to it. * * If ``cnt'' is -1, then there was one exact match. Its location is * put into ``loc'' and its entry is returned. * * If ``cnt'' is zero, then there were no matches for ``name'', but ``loc'' * is set to the location in the array in which you could place the * specified name in the sorted list. * * If ``cnt'' is one, then there was one command that non-ambiguously * completes ``name'' * * If ``cnt'' is greater than one, then there was exactly ``cnt'' number * of entries that completed ``name'', but they are all ambiguous. * The entry that is lowest alphabetically is returned, and its * location is put into ``loc''. */ array_item * find_array_item (array *set, const char *name, int *cnt, int *loc) { size_t len = strlen(name); int c = 0, pos = 0, tospot, /* :-) */ min, max; u_32int_t mask; u_32int_t hash; if (set->hash == HASH_INSENSITIVE) hash = ci_alist_hash(name, &mask); else hash = cs_alist_hash(name, &mask); *cnt = 0; if (!set->list || !set->max) { *loc = 0; return NULL; } alist_searches++; /* * The first search is a cheap refinement. Using the hash * value, find a range of symbols that have the same first * four letters as 'name'. Since we're doing all integer * comparisons, its cheaper than the full blown string compares. */ /* * OK, well all of that has been rolled into these two loops, * designed to find the start and end of the range directly. */ tospot = max = set->max - 1; min = 0; while (max >= min) { bin_ints++; pos = (max - min) / 2 + min; c = (hash & mask) - (ARRAY_ITEM(set, pos)->hash & mask); if (c == 0) { bin_chars++; c = set->func(name, ARRAY_ITEM(set, pos)->name, len); } if (c == 0) { if (max == pos) break; max = pos; } else if (c < 0) tospot = max = pos - 1; else min = pos + 1; } /* * If we can't find a symbol that qualifies, then we can just drop * out here. This is good because a "pass" (lookup for a symbol that * does not exist) requires only cheap integer comparisons. */ if (c != 0) { if (c > 0) *loc = pos + 1; else *loc = pos; return NULL; } /* * At this point, min is set to the first matching name in * the range and tospot is set to a higher position than * the last. These are used to refine the next search. */ max = tospot; tospot = pos; while (max >= min) { bin_ints++; pos = (min - max) / 2 + max; /* Don't ask */ c = (hash & mask) - (ARRAY_ITEM(set, pos)->hash & mask); if (c == 0) { bin_chars++; c = set->func(name, ARRAY_ITEM(set, pos)->name, len); } if (c == 0) { if (min == pos) break; min = pos; } else if (c < 0) max = pos - 1; else min = pos + 1; } min = tospot; char_searches++; /* * If we've gotten this far, then we've found at least * one appropriate entry. So we set *cnt to one */ *cnt = 1 + pos - min; #if 0 /* * So we know that 'pos' is a match. So we start 'min' * at one less than 'pos' and walk downard until we find * an entry that is NOT matched. */ min = pos - 1; while (min >= 0 && !set->func(name, ARRAY_ITEM(set, min)->name, len)) (*cnt)++, min--, lin_chars++; min++; /* * Repeat the same ordeal, except this time we walk upwards * from 'pos' until we dont find a match. */ max = pos + 1; while (max < set->max && !set->func(name, ARRAY_ITEM(set, max)->name, len)) (*cnt)++, max++, lin_chars++; #endif /* * Alphabetically, the string that would be identical to * 'name' would be the first item in a string of items that * all began with 'name'. That is to say, if there is an * exact match, its sitting under item 'min'. So we check * for that and whack the count appropriately. */ if (0 == (ARRAY_ITEM(set, min)->name)[len]) *cnt *= -1; /* * Then we tell the caller where the lowest match is, * in case they want to insert here. */ if (loc) *loc = min; /* * Then we return the first item that matches. */ return ARRAY_ITEM(set, min); }