#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;
}