#help_index "DolDoc/Output;StdOut/DolDoc"

CDocEntry *DocPutS(CDoc *doc, U8 *st)
{//Don't use this.  Use DocPrint().
//Does not handle partial Doc entries.
//Returns last newly created dollar-sign CDocEntry or NULL.
    U8          *ptr = st, *ptr2, *st2, *ptr3, *ptr4, *src, *char_bmp;
    Bool         unlock;
    I64          ch, j;
    CDocEntry   *doc_e = NULL, *res = NULL, *doc_ce;

    if (!st || !doc && !(doc = DocPut) || doc->doc_signature != DOC_SIGNATURE_VAL)
        return NULL;
    unlock = DocLock(doc);
    if (doc->flags & DOCF_PLAIN_TEXT_TABS)
        char_bmp = char_bmp_zero_cr_nl_cursor;
    else if (doc->flags & DOCF_PLAIN_TEXT)
        char_bmp = char_bmp_zero_tab_cr_nl_cursor;
    else
        char_bmp = char_bmp_zero_tab_cr_nl_cursor_dollar;
    doc_ce = doc->cur_entry;
    while (*ptr)
    {
        ptr2 = ptr;
        do ch = *ptr++;
        while (!Bt(char_bmp, ch) || ch == CH_CURSOR && doc->flags & DOCF_NO_CURSOR);
        ptr--;
        if (!ch)
        {
            if (j = ptr - ptr2)
            {
                doc_e = DocEntryNewBase(doc, DOCT_TEXT | doc->settings_head.default_text_attr << 8);
                if (doc->flags & DOCF_NO_CURSOR)
                {
                    src = MAlloc(j + 1);
                    MemCopy(src, ptr2, j + 1);
                    StrUtil(src, SUF_REM_CTRL_CHARS);
                    j = StrLen(src);
                }
                else
                    src = ptr2;
                doc_e->tag = MAlloc(j + 1, doc->mem_task);
                MemCopy(doc_e->tag, src, j + 1);
                doc_e->max_col = j;
                DocInsEntry(doc, doc_e);
                if (doc->flags & DOCF_NO_CURSOR)
                    Free(src);
            }
        }
        else
        {
            if (j = ptr - ptr2)
            {
                *ptr = 0;
                doc_e = DocEntryNewBase(doc, DOCT_TEXT | doc->settings_head.default_text_attr << 8);
                if (doc->flags & DOCF_NO_CURSOR)
                {
                    src = MAlloc(j + 1);
                    MemCopy(src, ptr2, j + 1);
                    ptr3 = src;
                    ptr4 = src;
                    while (*ptr3)
                        if (*ptr3 != CH_CURSOR)
                            *ptr4++ = *ptr3++;
                        else
                            ptr3++;
                    *ptr4 = 0;
                    j = ptr4 - src;
                }
                else
                    src = ptr2;
                doc_e->tag = MAlloc(j + 1, doc->mem_task);
                MemCopy(doc_e->tag, src, j + 1);
                doc_e->max_col = j;
                DocInsEntry(doc, doc_e);
                if (doc->flags & DOCF_NO_CURSOR)
                    Free(src);
                *ptr = ch;
            }
            switch (ch)
            {
                case CH_CURSOR:
                    doc_e = DocEntryNewBase(doc, DOCT_CURSOR | doc->settings_head.default_text_attr << 8);
                    DocInsEntry(doc, doc_e);
                    ptr++;
                    break;

                case '\t':
                    doc_e = DocEntryNewBase(doc, DOCT_TAB | doc->settings_head.default_text_attr << 8);
                    DocInsEntry(doc, doc_e);
                    ptr++;
                    break;

                case '$':
                    ptr++; //skip first dollar
                    ptr2 = ptr;
                    while (*ptr && *ptr != '$')
                        ptr++;
                    if (*ptr)
                    {
                        *ptr = 0; //zero second dollar
                        if (ptr - 1 == ptr2 && *ptr2 == CH_CURSOR)
                        {
                            doc_e = DocEntryNewBase(doc, DOCT_CURSOR | doc->settings_head.default_text_attr << 8);
                            DocInsEntry(doc, doc_e);
                            ptr2++;
                        }
                        if (ptr == ptr2)
                        {
                            doc_e = DocEntryNewBase(doc, DOCT_TEXT | doc->settings_head.default_text_attr << 8);
                            doc_e->max_col = 1;
                            if (doc->flags & DOCF_DBL_DOLLARS)
                                doc_e->tag = StrNew("$$", doc->mem_task);
                            else
                                doc_e->tag = StrNew("$", doc->mem_task);
                            DocInsEntry(doc, doc_e);
                        }
                        else
                        {
                            st2 = MAlloc(ptr-ptr2+1);
                            ptr3 = ptr2;
                            ptr4 = st2;
                            while (ch = *ptr3++)
                            {
                                if (ch == CH_CURSOR)
                                {
                                    doc_e = DocEntryNewBase(doc, DOCT_CURSOR | doc->settings_head.default_text_attr << 8);
                                    DocInsEntry(doc, doc_e);
                                }
                                else
                                    *ptr4++ = ch;
                            }
                            *ptr4 = 0;
                            if (doc_e = ParseDollarCmd(doc, st2))
                            {
                                res = doc_e;
                                DocInsEntry(doc, doc_e);
                            }
                            Free(st2);
                        }
                        *ptr++ = '$';
                    }
                    break;

                default:
                    doc_e = DocEntryNewBase(doc, DOCT_NEW_LINE | doc->settings_head.default_text_attr << 8);
                    DocInsEntry(doc, doc_e);
                    if (ch == '\r')
                        while (*ptr == '\r')
                            ptr++;
                    if (*ptr == '\n')
                        ptr++;
                    while (*ptr == '\r')
                        ptr++;
            }
        }
    }
    if (unlock)
        DocUnlock(doc);

    return res;
}

public CDocEntry *DocPrint(CDoc *doc=NULL, U8 *format, ...)
{//You must not print partial doc cmds.
//Returns last newly created dollar-sign CDocEntry or NULL.
    U8          *buf = StrPrintJoin(NULL, format, argc, argv);
    CDocEntry   *res = DocPutS(doc, buf);

    Free(buf);

    return res;
}

public U0 DocPrintPartial(CDoc *doc=NULL, U8 *format, ...)
{//Lets you print half a doc cmd,  if you like.
    U8          *buf, *st, *src, *dst, *ptr, *ptr2;
    Bool         unlock;
    CDocEntry   *doc_ce, *doc_ne;
    I64          ch, i, j;
    if (!doc && !(doc = DocPut))
        return;
    buf = StrPrintJoin(NULL, format, argc, argv);
    ptr = buf;
    if (doc->user_put_s && (*doc->user_put_s)(doc, doc->user_put_data, buf))
    {
        Free(buf);
        return;
    }
    unlock = DocLock(doc);
    if (doc->cur_entry->type_u8 == DOCT_DATA)
        while (ch = *ptr++)
            DocPutKey(doc, ch, 0);
    else
        while (ch = *ptr)
        {
            if (!Bt(char_bmp_safe_dollar, ch) || doc->flags & (DOCF_OVERSTRIKE | DOCF_IN_DOLLAR))
            {
                DocPutKey(doc, ch, 0);
                ptr++;
            }
            else
            {
                ptr2 = ptr++;
                while (TRUE)
                {
                    ch = *ptr++;
                    if (!Bt(char_bmp_safe_dollar, ch))
                        break;
                }
                ptr--;
                *ptr = 0;
                doc_ce = doc->cur_entry;
                j = ptr-ptr2;
                if (IsEditableText(doc_ce))
                {
                    dst = st = MAlloc(doc_ce->max_col + j + 1, doc->mem_task);
                    src = doc_ce->tag;
                    i = doc->cur_col;
                    doc->cur_col += j;
                    doc_ce->max_col += j;
                    while (i-- > 0)
                        *dst++ = *src++;
                    while (j-- > 0)
                        *dst++ = *ptr2++;
                    while (*dst++ = *src++);
                    Free(doc_ce->tag);
                    doc_ce->tag = st;
                }
                else
                {
                    doc_ne = DocEntryNewTag(doc, doc_ce, ptr2);
                    doc_ne->type = DOCT_TEXT | doc->settings_head.default_text_attr << 8;
                    doc_ne->de_flags = doldoc.default_de_flags[DOCT_TEXT];
                    QueueInsert(doc_ne, doc_ce->last);
                    doc->cur_entry = doc_ne;
                    doc->cur_col = StrLen(ptr2);
                }
                *ptr = ch;
                DocRemSoftNewLines(doc, doc->cur_entry);
            }
        }
    if (unlock)
        DocUnlock(doc);
    if (!(doc->flags & DOCF_DONT_SWAP_OUT))
        Yield;
    Free(buf);
}

Bool KDDocPutS(U8 *st)
{
    CDoc *doc;

    if (doc = DocPut)
        DocPrintPartial(doc, "%s", st);

    return FALSE;
}

public U0 DocPrintAtomic(CDoc *doc=NULL, U8 *format, ...)
{//Prints multiple whole cmds all-at-once. Might need this when printing trees.
    U8  *buf;
    Bool unlock;
    I64  old_flags;

    if (!doc && !(doc = DocPut))
        return;
    buf = StrPrintJoin(NULL, format, argc, argv);
    unlock = DocLock(doc);
    old_flags = doc->flags;
    doc->flags |= DOCF_NO_CURSOR;
    DocPrint(doc, "%s", buf);
    DocRecalc(doc);
    doc->flags = old_flags;
    if (unlock)
        DocUnlock(doc);
    Free(buf);
}

U0 DocDumpLines(CDoc *doc, I64 uS_delay=0)
{
    U8          *st;
    CDocEntry   *doc_e, *doc_e2;
    Bool         unlock = DocLock(doc);

    doc_e = doc->head.next;
    while (doc_e != doc)
    {
        st = DocScanLine(doc, doc_e, NULL, &doc_e2);
        "%s", st;
        Free(st);
        doc_e = doc_e2;
        if (doc_e->type_u8 == DOCT_NEW_LINE)
        {
            '\n';
            Busy(uS_delay);
            doc_e = doc_e->next;
        }
    }
    if (unlock)
        DocUnlock(doc);
}

public CDocEntry *DocPutLine(CDoc *doc=NULL, CDocEntry *doc_e)
{//Send line from other doc to StdOut DocPut.
    I64  ch;
    U8  *ptr, *ptr2;
    Bool unlock;

    if (!doc && !(doc = DocPut) || doc->doc_signature != DOC_SIGNATURE_VAL)
        return NULL;
    unlock = DocLock(doc);
    while (doc_e != doc && doc_e->type_u8 != DOCT_NEW_LINE)
    {
        if (doc_e->de_flags & DOCEF_TAG)
        {
            ptr = doc_e->tag;
            do
            {
                ptr2 = ptr;
                while (ch = *ptr)
                    if (ch == '$')
                        break;
                    else
                        ptr++;
                *ptr = 0;
                "%s", ptr2;
                *ptr = ch;
                if (ch == '$')
                {
                    "$$";
                    ptr++;
                }
            }
            while (ch);
        }
        else if (doc_e->type_u8 == DOCT_TAB)
            '\t';
        doc_e = doc_e->next;
    }
    '\n';
    if (doc_e != doc)
        doc_e = doc_e->next;
    if (unlock)
        DocUnlock(doc);

    return doc_e;
}

#help_index "Debugging/Dump;DolDoc/Cmd Line (Typically);"\
            "Cmd Line (Typically);DolDoc/Output;StdOut/DolDoc"
public U0 DocDumpMem(U8 *buf, I64 count=0x80)
{//Dump live chunk of mem showing addresses. Can be edited.
    CDocEntry   *doc_e;
    CDoc        *doc = DocPut;
    Bool         unlock = DocLock(doc);

    doc_e = DocPrint(doc, "$HX-Z,%d,16$", count);
    doc_e->data = buf;
    doc->cur_entry = doc_e->next;
    DocRecalc(doc);
    if (unlock)
        DocUnlock(doc);
}

public U0 DocDump(U8 *buf, I64 count=0x80)
{//Dump live chunk of mem showing offsets. Can be edited.
    CDocEntry   *doc_e;
    CDoc        *doc = DocPut;
    Bool         unlock = DocLock(doc);

    doc_e = DocPrint(doc, "$HX,%d,16$", count);
    doc_e->data = buf;
    doc->cur_entry = doc_e->next;
    DocRecalc(doc);
    if (unlock)
        DocUnlock(doc);
}