#help_index "Cmd Line (Typically)"

#define FND_REPLACE     0
#define FND_SKIP        1
#define FND_ALL         2
#define FND_ED          3
#define FND_ABORT_FILE  4
#define FND_SKIP_FILE   5

I64 PopUpFindMenu()
{
    I64      i;
    CDoc    *doc = DocNew;

    DocPrint(doc,   "$CM+LX,2,4$$BT,\"REPLACE\",LE=FND_REPLACE$"
                    "$CM+LX,22,0$$BT,\"SKIP\",LE=FND_SKIP$"
                    "$CM+LX,2,4$$BT,\"ALL\",LE=FND_ALL$"
                    "$CM+LX,22,0$$BT,\"ABORT ALL\",LE=DOCM_CANCEL$"
                    "$CM+LX,2,4$$BT,\"EDIT\",LE=FND_ED$"
                    "$CM+LX,22,0$$BT,\"ABORT FILE\",LE=FND_ABORT_FILE$"
                    "$CM+LX,2,4$$BT,\"SKIP FILE\",LE=FND_SKIP_FILE$\n");
    i = PopUpMenu(doc);
    DocDel(doc);

    return i;
}

I64 FindFile(U8 *needle_str, U8 *haystack_filename, I64 *_fuf_flags, U8 *replace_text)
{//Have you confused with FileFind()?
    Bool         first_on_line, write_this_file = FALSE, cont = !Bt(_fuf_flags, FUf_CANCEL);
    U8          *src, *dst, *dst2, *name_buf = NULL;
    I64          i, j, plen, rlen, dlen, count = 0,old_flags, ss_flags;
    CDoc        *cur_l, *doc = DocRead(haystack_filename, DOCF_PLAIN_TEXT_TABS | DOCF_NO_CURSOR);
    CDocEntry   *doc_e;

    if (Bt(_fuf_flags, FUf_IGNORE))
        ss_flags = SFF_IGNORE_CASE;
    else
        ss_flags = 0;
    if (Bt(_fuf_flags, FUf_WHOLE_LABELS))
        ss_flags |= SFG_WHOLE_LABELS;
    if (Bt(_fuf_flags, FUf_WHOLE_LABELS_BEFORE))
        ss_flags |= SFF_WHOLE_LABELS_BEFORE;
    if (Bt(_fuf_flags, FUf_WHOLE_LABELS_AFTER))
        ss_flags |= SFF_WHOLE_LABELS_AFTER;

    plen = StrLen(needle_str);
    if (replace_text)
        rlen = StrLen(replace_text);
    doc_e = doc->head.next;
    while (doc_e != doc && cont)
    {
        if (doc_e->type_u8 == DOCT_TEXT)
        {
            src = doc_e->tag;
            first_on_line = TRUE;
            while (src && cont)
            {
                if (src = StrFind(needle_str, src, ss_flags))
                {
                    count++;
                    if (first_on_line || Bt(_fuf_flags, FUf_REPLACE))
                    {
                        first_on_line = FALSE;
                        PutFileLink(haystack_filename,, doc_e->y + 1, TRUE);
                        name_buf = MStrPrint("%s,%d", haystack_filename, doc_e->y + 1);
                        if (cur_l = DocPut)
                        {
                            old_flags = cur_l->flags & DOCF_PLAIN_TEXT;
                            cur_l->flags |= DOCF_PLAIN_TEXT;
                        }
                        " %s\n", doc_e->tag;
                        if (cur_l)
                            cur_l->flags = cur_l->flags & ~DOCF_PLAIN_TEXT | old_flags;
                    }
                    if (Bt(_fuf_flags, FUf_REPLACE))
                    {
                        if (Bt(_fuf_flags, FUf_ALL))
                            i = FND_ALL;
                        else
                        {
                            i = PopUpFindMenu;
                            if (i < 0)
                            {
                                LBts(_fuf_flags, FUf_CANCEL);
                                cont = FALSE;
                                write_this_file = FALSE;
                            }
                            else if (i == FND_ALL)
                                LBts(_fuf_flags, FUf_ALL);
                            else if (i == FND_ABORT_FILE)
                            {
                                cont = FALSE;
                                write_this_file = FALSE;
                            }
                            else if (i == FND_SKIP_FILE)
                                cont=FALSE;
                        }
                        if (i == FND_REPLACE || i == FND_ALL)
                        {
                            dlen = StrLen(doc_e->tag);
                            dst = MAlloc(dlen + 1 + rlen - plen);
                            dst2 = dst;
                            j = src - doc_e->tag;
                            for (i = 0; i < j; i++)
                                *dst++ = doc_e->tag[i];
                            for (i = 0; i < rlen; i++)
                                *dst++ = replace_text[i];
                            src = dst;
                            for (i = j + plen; i <= dlen; i++)
                                *dst++ = doc_e->tag[i];
                            Free(doc_e->tag);
                            doc_e->tag = dst2;
                            if (cur_l = DocPut)
                            {
                                old_flags = cur_l->flags & DOCF_PLAIN_TEXT;
                                cur_l->flags |= DOCF_PLAIN_TEXT;
                            }
                            "%12s,%04d*%s\n", haystack_filename, doc_e->y + 1, dst2;
                            if (cur_l)
                                cur_l->flags = cur_l->flags & ~DOCF_PLAIN_TEXT | old_flags;
                            write_this_file = TRUE;
                        }
                        else
                        {
                            src++;
                            if (i == FND_ED)
                            {
                                Free(name_buf);
                                name_buf = StrNew(doc->filename.name);
                                doc->flags      &= ~DOCF_NO_CURSOR;
                                doc->cur_entry  =  doc_e;
                                doc->cur_col    =  doc_e->min_col;
                                DocWrite(doc);
                                DocDel(doc);
                                "Wrote:%s\n", name_buf;
                                Ed(name_buf);
                                doc = DocRead(name_buf, DOCF_PLAIN_TEXT_TABS);
                                doc_e = doc->cur_entry;
                                if (doc_e->last != doc)
                                    doc_e = doc_e->last;
                                src = NULL;
                                write_this_file = FALSE;
                            }
                        }
                    }
                    else
                        src++;
                    Free(name_buf);
                    name_buf = NULL;
                }
            }
        }
        doc_e = doc_e->next;
    }
    if (write_this_file)
    {
        DocWrite(doc);
        "Wrote:%s\n", doc->filename.name;
    }
    DocDel(doc);

    return count;
}

public I64 Find(U8 *needle_str, U8 *files_find_mask="*", U8 *fu_flags=NULL, U8 *replace_text=NULL)
{/*Find occurrences of a string in files.
This does not do regular expressions.
Anyway, it's good for searching and replacing.
Let's say it stands for global replace ;-)

"+r" =recurse
"+i" =ignore case
"+l" =whole labels only.
This will check for a nonlabel character before
and after.  If you have a variable, "dd" and don't
want to match words like "Add", you
set this flag and it will see that the characters
before or after "dd" are label characters.
"+lb"=only checks for label chars before.
"+la"=only checks for label chars after.
*/
    I64          count = 0, fuf_flags = 0;
    CDirEntry   *tmpde, *tmpde1;

    FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), "+r+i+f+F+T");
    FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), fu_flags);
    if (fuf_flags & ~(FUG_FILES_FIND | FUF_IGNORE | FUF_ALL |
                      FUF_WHOLE_LABELS | FUF_WHOLE_LABELS_BEFORE | FUF_WHOLE_LABELS_AFTER))
        throw('FUF');
    LBEqual(&fuf_flags, FUf_REPLACE, replace_text);
    tmpde = tmpde1 = FilesFind(files_find_mask, fuf_flags & FUG_FILES_FIND);
    fuf_flags &= FUF_ALL | FUF_REPLACE | FUF_IGNORE | FUF_WHOLE_LABELS | FUF_WHOLE_LABELS_BEFORE | FUF_WHOLE_LABELS_AFTER;
    while (tmpde && !Bt(&fuf_flags, FUf_CANCEL))
    {
        count += FindFile(needle_str, tmpde->full_name, &fuf_flags, replace_text);
        tmpde = tmpde->next;
    }
    DirTreeDel(tmpde1);

    return count;
}

public I64 FileOcc(U8 *needle_str, U8 *files_find_mask="*", U8 *fu_flags="+r+i+l")
{/*Silently return occurrences of a string in files.
"+r"=recurse
"+i"=ignore case
"+l"=whole labels only.
"+lb"=only checks for label chars before.
"+la"=only checks for label chars after.
*/
    I64  count = 0;
    Bool old_silent = Silent(TRUE);

    count = Find(needle_str, files_find_mask, fu_flags);
    Silent(old_silent);

    return count;
}

class CFind
{
    U8      find_text[STR_LEN]      format "$DA-P,"
                                           "A=\"FIND        :%s\"$\n";
    U8      replace_text[STR_LEN]   format "$DA-P,"
                                           "A=\"REPLACE     :%s\"$\n";
    Bool    replace                 format "$CB,\"REPLACE\"$\n";
    Bool    match_case              format "$CB,\"MATCH CASE\"$\n";
    Bool    whole_labels            format "$CB,\"WHOLE LABELS\"$\n";
    U8      filemask[STR_LEN]       format "$DA-P,A=\"FILE MASK   :%s\"$\n";
    Bool    recurse                 format "$CB,\"RECURSE\"$\n";
};

I64 FindWiz()
{
    CDoc    *doc;
    U8       buf[32], *dir, *st;
    CFind   *g = CAlloc(sizeof(CFind));
    I64      res = 0;

    g->recurse = TRUE;
    StrCopy(g->filemask, FILEMASK_TXT);
    if (doc = DocPut)
    {
        StrCopy(g->find_text,    doc->find_replace->find_text);
        StrCopy(g->replace_text, doc->find_replace->replace_text);
        g->replace      = doc->find_replace->replace;
        g->match_case   = doc->find_replace->match_case;
        g->whole_labels = doc->find_replace->whole_labels;
    }
    if (DocForm(g,, 0, "$PURPLE$$TX+CX,\"Find\"$\n$FG$"))
    {
        if (doc)
        {
            StrCopy(doc->find_replace->find_text,    g->find_text);
            StrCopy(doc->find_replace->replace_text, g->replace_text);
            doc->find_replace->replace      = g->replace;
            doc->find_replace->match_case   = g->match_case;
            doc->find_replace->whole_labels = g->whole_labels;
        }
        dir = PopUpPickDir;
        if (*dir)
        {
            *buf = 0;
            if (g->match_case)
                CatPrint(buf, "-i");
            if (!g->recurse)
                CatPrint(buf, "-r");
            if (g->whole_labels)
                CatPrint(buf, "+l");
            if (g->replace)
                st = MStrPrint("\"$$WW+H,1$$\";Cd(\"%s\");Find(\"%Q\",\"%Q\",\"%Q\",\"%Q\");UserTaskCont;",
                               dir, g->find_text, g->filemask, buf, g->replace_text);
            else
                st = MStrPrint("\"$$WW+H,1$$\";Cd(\"%s\");Find(\"%Q\",\"%Q\",\"%Q\");UserTaskCont;",
                               dir, g->find_text, g->filemask, buf);
            res = PopUp(st);
        }
        Free(dir);
    }
    Free(g);

    return res;
}

public I64 FR(U8 *text_to_replace, U8 *new_text, U8 *files_find_mask="/*", U8 *fu_flags=NONE, I64 sff_flags=NONE)
{//Files rename, Rename files matching mask.
 //Example: FR("Disk", "Disk");
 //FR("Gr", "Graphics", "/System/Gr/*");
    CDirEntry   *files, *files_head;
    I64          i, count = 0, fuf_flags = 0;
    U8          *tmp_name, *new_path;
    Bool         all_flag = FALSE;

    FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), "+r+f+F");
    FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), fu_flags);

    files = files_head = FilesFind(files_find_mask, fuf_flags);

    while (files)
    {
        if (StrFind(text_to_replace, files->name, sff_flags))
        {
            PutFileLink(files->full_name);
            ' -> ';
            tmp_name = StrReplace(files->name, text_to_replace, new_text, sff_flags);
            new_path = MStrPrint("%s/%s", DirFile(files->full_name), tmp_name);
            PutFileLink(new_path);

fr_all:
            if (all_flag)
                i = FND_REPLACE;
            else
                i = PopUpFindMenu;

            switch(i)
            {
                case FND_ALL:
                    all_flag = TRUE;
                    goto fr_all;

                case FND_REPLACE:
                    " $LTGREEN$*$FG$\n";
                    Move(files->full_name, new_path);
                    count++;
                    break;
                case FND_SKIP:
                case FND_SKIP_FILE:
                case FND_ABORT_FILE:
                    '\n';
                    break;
                case DOCM_CANCEL:
                    Free(tmp_name);
                    Free(new_path);
                    DirTreeDel(files_head);
                    '\n';

                    return count;
            }
            Free(tmp_name);
            Free(new_path);
            '\n';
        }
        files = files->next;
    }
    DirTreeDel(files_head);

    return count;
}