/*
 * Copyright 2002 Silicon Graphics, Inc. All rights reserved.
 */
#include <kl_lib.h>

#define ST_BLKSZ	4096

/*
 * kl_init_string_table()
 */
string_table_t *
kl_init_string_table(int flag)
{
	string_table_t *st;

	if (flag == K_PERM) {
		st = (string_table_t*)	
			kl_alloc_block(sizeof(string_table_t), K_PERM);
		if (st) {
			st->block_list = kl_alloc_block(ST_BLKSZ, K_PERM);
		}
	} else {
		st = (string_table_t*)
			kl_alloc_block(sizeof(string_table_t), K_TEMP);
		if (st) {
			st->block_list = kl_alloc_block(ST_BLKSZ, K_TEMP);
		}
	}
	return(st);
}

/*
 * kl_free_string_table()
 */
void
kl_free_string_table(string_table_t *st)
{
	void *block, *next_block;

	block = st->block_list;

	while (block) {
		next_block = *(void**)block;
		kl_free_block(block);
		block = next_block;
	}
	kl_free_block((void *)st);
}

/*
 * in_block()
 */
static int
in_block(char *s, void *block)
{
	if ((s >= (char*)block) && (s < ((char*)block + ST_BLKSZ))) {
		return(1);
	}
	return(0);
}

/*
 * kl_get_string()
 */
char *
kl_get_string(string_table_t *st, char *str, int flag)
{
	void *block, *last_block;
	char *s;

	/* If, for some reason, st is NULL, then just allocate a block of
	 * type flag (this "feature" allows for string tables to be used
	 * or not used with the same code).
	 */
	if (!st) {
		s = (char *)kl_str_to_block(str, flag);
		return(s);
	}

	block = st->block_list;
	s = (char *)((long)block + sizeof(long));

	/* Walk the strings in the table looking for str. If we find it
	 * return a pointer to the string.
	 */
	while (*s) {
		if (!strcmp(str, s)) {
			return(s);
		}
		s += strlen(s) + 1;

		if (!in_block((s + strlen(str) + 1), block)) {
			last_block = block;
			block = *(void**)last_block;
			if (!block) {
				break;
			}
			s = (char *)((long)block + sizeof(long));
		}
	} 

	/* If we didn't find the string, we have to add it to the
	 * table and then return a pointer to it. If we are still
	 * in the middle of a block, make sure there is enough room
	 * for the new string. If there isn't, allocate a new block
	 * and put the string there (after linking in the new block).
	 */
	if (block) {
		if (in_block((s + strlen(str)), block)) {
			strcpy(s, str);
			st->num_strings++;
			return(s);
		} else {
			last_block = block;
			block = *(void **)block;
		}
	}

	/* If we get here, it means that there wasn't enough string in 
	 * an existing block for the new string. So, allocate a new block
	 * and put the string there.
	 */
	if (flag & K_PERM) {
		block = kl_alloc_block(ST_BLKSZ, K_PERM);
	} else {
		block = kl_alloc_block(ST_BLKSZ, K_TEMP);
	}
	*(void **)last_block = block;

	s = (char *)((long)block + sizeof(long));
	strcpy(s, str);
	st->num_strings++;
	return(s);
}
