#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)
	{
		StrFirstRemove(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 && !StrCompare(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)
	{
		StrFirstRemove(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 && !StrCompare(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)
		{
			StrFirstRemove(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 *format, I64 argc, I64 *argv)
{//Rewrite doc tree branch.
	CDocEntry	*tree_branch, *start_indent, *end_indent;
	U8			*buf = StrPrintJoin(NULL, format, 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 && DriveIsWritable(*doc->filename.name))
		DocWrite(doc);
	if (unlock_doc)
		DocUnlock(doc);
	Free(buf);

	return res;
}

Bool DocTreeAppendJoin(CDoc *doc, U8 *path, Bool write, U8 *format, I64 argc, I64 *argv)
{//Append to doc tree branch.
	CDocEntry	*tree_branch, *start_indent, *end_indent;
	U8			*buf = StrPrintJoin(NULL, format, 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 && DriveIsWritable(*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 *format, ...)
{//Rewrite doc tree branch.
	return DocTreeWriteJoin(doc, path, write, format, argc, argv);
}

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

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

	DocDel(doc);

	return res;
}

public Bool DocTreeFAppend(U8 *name, U8 *path, U8 *format, ...)
{//Append to doc tree branch in file.
	CDoc *doc = DocRead(name);
	Bool  res = DocTreeAppendJoin(doc, path, TRUE, format, 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);
	CCompCtrl	*cc = CompCtrlNew(, ccf_flags | CCF_DONT_FREE_BUF);

	if (Fs->last_cc != &Fs->next_cc)
		cc->opts = Fs->last_cc->opts;
	QueueInsert(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;
		}
	}
	QueueRemove(cc);
	if (okay)
		CompCtrlDel(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;
}
