#define SFT_GENERIC 		1

public U8 **StrFileRead(U8 *name, I64 *_max_num=NULL, U8 **_colors=NULL, Bool no_nums=FALSE)
{
	CDoc		*doc = DocRead(name, DOCF_DBL_DOLLARS | DOCF_NO_CURSOR);
	CDocEntry	*doc_e = doc->head.next;
	I64			 i, max_num = 0;
	U8			*ptr, **res, *colors;

	while (doc_e != doc)
	{
		if (doc_e->type_u8 == DOCT_TEXT)
		{
			if (no_nums)
				i = ++max_num;
			else
			{
				i = Str2I64(doc_e->tag,, &ptr);
				if (i > max_num)
					max_num = i;
				if (*ptr == ', ')
					ptr++;
				ptr = StrNew(ptr);
				Free(doc_e->tag);
				doc_e->tag = ptr;
			}
			doc_e->user_data = i;
		}
		doc_e = doc_e->next;
	}

	res = CAlloc(sizeof(U8 *)  * (max_num + 1));
	colors = CAlloc(sizeof(U8) * (max_num + 1));
	doc_e = doc->head.next;
	while (doc_e != doc)
	{
		if (doc_e->type_u8 == DOCT_TEXT && 0 <= doc_e->user_data <= max_num)
		{
			res[doc_e->user_data] = doc_e->tag;
			doc_e->tag = NULL;
			colors[doc_e->user_data] = doc_e->type.u8[1] & 15;
		}
		doc_e = doc_e->next;
	}

	DocDel(doc);
	if (_max_num)
		*_max_num = max_num;
	if (_colors)
		*_colors = colors;
	else
		Free(colors);

	return res;
}

public U0 StrFileArrDel(U8 **a, I64 max_num)
{
	I64 i;

	for (i = 0; i <= max_num; i++)
		Free(a[i]);
	Free(a);
}

public I64 StrFileAdd(U8 *st, I64 *_num, CHashTable *table, I64 color=COLOR_INVALID)
{
	CHashGeneric *tmph;

	if (!st)
		return 0;
	if (!(tmph = HashFind(st, table, SFT_GENERIC)))
	{
		tmph = CAlloc(sizeof(CHashGeneric));
		tmph->type			= SFT_GENERIC;
		tmph->str			= StrNew(st);
		tmph->user_data0	= (*_num)++;
		HashAdd(tmph, table);
	}
	if (color != COLOR_INVALID)
		tmph->user_data1 = color;

	return tmph->user_data0;
}

I64 StrEntriesCompare(CHashGeneric *h1, CHashGeneric *h2)
{
	return h1->user_data0 - h2->user_data0;
}

public U0 StrFileWrite(U8 *name, CHashTable *table, Bool no_nums=FALSE)
{
	I64				 i, j, count, color = BLACK;
	CDoc			*doc = DocNew(name);
	CHashGeneric	*tmph, **a;

	if (table)
	{
		count = 0;		//Count Strings
		for (i = 0; i <= table->mask; i++)
			count += LinkedListCount(table->body[i]);
		a = MAlloc(count * sizeof(CHashGeneric *));
		j = 0;				//Load Strings
		for (i = 0; i <= table->mask; i++)
		{
			tmph = table->body[i];
			while (tmph)
			{
				a[j++] = tmph;
				tmph = tmph->next;
			}
		}
		QuickSortI64(a, count, &StrEntriesCompare);
		for (i = 0; i < count; i++)
		{
			tmph = a[i];
			if (tmph->user_data1 & 15 != color)
			{
				DocPrint(doc, "$$FG,%d$$", tmph->user_data1 & 15);
				color = tmph->user_data1 & 15;
			}
			if (no_nums)
				DocPrint(doc, "%s\n", tmph->str);
			else
				DocPrint(doc, "%d,%s\n", tmph->user_data0, tmph->str);
		}
		Free(a);
	}
	doc->flags |= DOCF_NO_CURSOR;
	DocWrite(doc);
	DocDel(doc);
}

public U0 StrFileDel(CHashTable *table)
{
	I64			  i;
	CHashGeneric *tmph, *tmph1;

	if (!table)
		return;
	for (i = 0; i <= table->mask; i++)
	{
		tmph = table->body[i];
		while (tmph)
		{
			tmph1=tmph->next;
			Free(tmph->str);
			Free(tmph);
			tmph = tmph1;
		}
	}
	Free(table->body);
	Free(table);
}