#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,cnt,color=BLACK;
  CDoc *doc=DocNew(name);
  CHashGeneric *tmph,**a;
  if (table) {
    cnt=0;      //Count Strings
    for (i=0;i<=table->mask;i++)
      cnt+=LinkedLstCnt(table->body[i]);
    a=MAlloc(cnt*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;
      }
    }
    QSortI64(a,cnt,&StrEntriesCompare);
    for (i=0;i<cnt;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);
}