#help_index "DolDoc/Tree"

public Bool DocTreeFind(CDoc *haystack_doc,U8 *needle_path,
  CDocEntry **_tree_entry=NULL,
  CDocEntry **_start_indent=NULL, CDocEntry **_end_indent=NULL)
{//Find tree widget start and end.
  I64 i=0,k=0;
  U8 *st1=StrNew(needle_path),*st2=MAlloc(StrLen(needle_path)+1);
  Bool res=FALSE,unlock_doc=DocLock(haystack_doc);
  CDocEntry *doc_e=haystack_doc->head.next;
  if (_tree_entry) *_tree_entry=haystack_doc;
  if (_start_indent) *_start_indent=haystack_doc;
  if (_end_indent) *_end_indent=haystack_doc;
  while (*st1 && doc_e!=haystack_doc) {
    StrFirstRem(st1,"/",st2);
    if (*st2) {
      while (doc_e!=haystack_doc) {
	if (doc_e->type_u8==DOCT_INDENT)
	  i+=doc_e->attr;
	else if (i==k && doc_e->de_flags&DOCEF_TREE &&
	      !StrCmp(doc_e->tag+3,st2)) {
	  if (*st1)
	    break;
	  else {
	    if (_tree_entry) *_tree_entry=doc_e;
	    i=0;
	    while (doc_e!=haystack_doc && doc_e->type_u8!=DOCT_INDENT)
	      doc_e=doc_e->next;
	    if (doc_e!=haystack_doc) {
	      i=doc_e->attr;
	      if (_start_indent) *_start_indent=doc_e;
	      doc_e=doc_e->next;
	      while (doc_e!=haystack_doc && i>0) {
		if (doc_e->type_u8==DOCT_INDENT) {
		  i+=doc_e->attr;
		  if (i<=0) {
		    if (_end_indent) *_end_indent=doc_e;
		    res=TRUE;
		    break;
		  }
		}
		doc_e=doc_e->next;
	      }
	    }
	    goto ft_done;
	  }
	}
	doc_e=doc_e->next;
      }
      k+=2;
    }
  }
ft_done:
  if (unlock_doc)
    DocUnlock(haystack_doc);
  Free(st1);
  Free(st2);
  return res;
}

public Bool DocTreeFFind(U8 *name,U8 *path)
{//Find tree widget in file.
  CDoc *doc=DocRead(name);
  Bool res=DocTreeFind(doc,path);
  DocDel(doc);
  return res;
}

public Bool DocTreeMake(CDoc *doc,U8 *path)
{//Make tree widget.
  I64 i=0,j=I64_MIN,k=0;
  U8 *st1=StrNew(path),
	*st2=MAlloc(StrLen(path)+1),
	*st3=StrNew(path);
  Bool res=TRUE,unlock_doc=DocLock(doc);
  CDocEntry *doc_e=doc->head.next;
  doc->cur_entry=doc;
  doc->cur_col=0;
  while (*st1 && doc_e!=doc) {
    StrFirstRem(st1,"/",st2);
    if (*st2) {
      while (doc_e!=doc) {
	if (doc_e->type_u8==DOCT_INDENT) {
	  i+=doc_e->attr;
	  if (i==j) {
	    doc->cur_entry=doc_e;
	    doc->cur_col=0;
	    goto mt_done;
	  }
	} else if (i==k && doc_e->de_flags&DOCEF_TREE &&
	      !StrCmp(doc_e->tag+3,st2)) {
	  Free(st3);
	  st3=StrNew(st1);
	  j=i;
	  if (!*st1)
	    res=FALSE;
	  else
	    break;
	}
	doc_e=doc_e->next;
      }
      k+=2;
    }
  }
mt_done:
  if (res) {
    while (*st3) {
      StrFirstRem(st3,"/",st2);
      if (*st2) {
	DocPrint(doc,"$$TR+C,\"%s\"$$\n$$ID,2$$",st2);
	doc->cur_entry=DocPrint(doc,"$$ID,-2$$");
	doc->cur_col=0;
      }
    }
  }
  if (unlock_doc)
    DocUnlock(doc);
  Free(st1);
  Free(st2);
  Free(st3);
  return res;
}

Bool DocTreeWriteJoin(CDoc *doc,U8 *path,Bool write,U8 *fmt,I64 argc,I64 *argv)
{//Rewrite doc tree branch.
  CDocEntry *tree_branch,*start_indent,*end_indent;
  U8 *buf=StrPrintJoin(NULL,fmt,argc,argv);
  Bool res,unlock_doc=DocLock(doc);
  if (res=DocTreeFind(doc,path,
	&tree_branch,&start_indent,&end_indent)) {
    DocCut(doc,start_indent->next,end_indent->last);
    doc->cur_entry=start_indent->next;
    doc->cur_col=doc->cur_entry->min_col;
  } else
    DocTreeMake(doc,path);
  DocPrint(doc,"%s",buf);
  if (write && DrvIsWritable(*doc->filename.name))
    DocWrite(doc);
  if (unlock_doc)
    DocUnlock(doc);
  Free(buf);
  return res;
}

Bool DocTreeAppendJoin(CDoc *doc,U8 *path,Bool write,U8 *fmt,I64 argc,I64 *argv)
{//Append to doc tree branch.
  CDocEntry *tree_branch,*start_indent,*end_indent;
  U8 *buf=StrPrintJoin(NULL,fmt,argc,argv);
  Bool res,unlock_doc=DocLock(doc);
  if (res=DocTreeFind(doc,path,
	&tree_branch,&start_indent,&end_indent)) {
    doc->cur_entry=end_indent;
    doc->cur_col=doc->cur_entry->min_col;
  } else
    DocTreeMake(doc,path);
  DocPrint(doc,"%s",buf);
  if (write && DrvIsWritable(*doc->filename.name))
    DocWrite(doc);
  if (unlock_doc)
    DocUnlock(doc);
  Free(buf);
  return res;
}

public Bool DocTreeWrite(CDoc *doc,U8 *path,Bool write=TRUE,U8 *fmt,...)
{//Rewrite doc tree branch.
  return DocTreeWriteJoin(doc,path,write,fmt,argc,argv);
}

public Bool DocTreeAppend(CDoc *doc,U8 *path,Bool write=TRUE,U8 *fmt,...)
{//Append to doc tree branch.
  return DocTreeAppendJoin(doc,path,write,fmt,argc,argv);
}

public Bool DocTreeFWrite(U8 *name,U8 *path,U8 *fmt,...)
{//Rewrite doc tree branch in file.
  CDoc *doc=DocRead(name);
  Bool res=DocTreeWriteJoin(doc,path,TRUE,fmt,argc,argv);
  DocDel(doc);
  return res;
}

public Bool DocTreeFAppend(U8 *name,U8 *path,U8 *fmt,...)
{//Append to doc tree branch in file.
  CDoc *doc=DocRead(name);
  Bool res=DocTreeAppendJoin(doc,path,TRUE,fmt,argc,argv);
  DocDel(doc);
  return res;
}

#help_index "DolDoc/Compiler;Compiler"
public I64 ExeDoc(CDoc *doc,I64 ccf_flags=0)
{//JIT Compile and execute a document.
  I64 res;
  Bool okay=TRUE,unlock_doc=DocLock(doc);
  CCmpCtrl *cc=CmpCtrlNew(,ccf_flags|CCF_DONT_FREE_BUF);
  if (Fs->last_cc!=&Fs->next_cc)
    cc->opts=Fs->last_cc->opts;
  QueIns(cc,Fs->last_cc);
  LexAttachDoc(cc,,doc);
  try {
    Lex(cc);
    res=ExeCmdLine(cc);
  } catch {
    if (Fs->except_ch=='Compiler' || Fs->except_ch=='Break') {
      Fs->catch_except=TRUE;
      okay=FALSE;
      res=0;
    }
  }
  QueRem(cc);
  if (okay)
    CmpCtrlDel(cc); //TODO: can crash
  if (unlock_doc)
    DocUnlock(doc);
  return res;
}

#help_index "DolDoc/Tree;DolDoc/Compiler;Compiler"
public I64 DocTreeExe(CDoc *doc,U8 *path)
{//Execute doc tree branch.
  CDoc *doc2;
  Bool unlock_doc=DocLock(doc);
  CDocEntry *tree_branch,*start_indent,*end_indent;
  I64 res=0;
  if (DocTreeFind(doc,path,&tree_branch,&start_indent,&end_indent)) {
    doc2=DocCopy(doc,tree_branch,end_indent);
    res=ExeDoc(doc2);
    DocDel(doc2);
  }
  if (unlock_doc)
    DocUnlock(doc);
  return res;
}

public I64 DocTreeFExe(U8 *name,U8 *path)
{//Execute doc tree branch in file.
  I64 res;
  CDoc *doc=DocRead(name);
  res=DocTreeExe(doc,path);
  DocDel(doc);
  return res;
}