#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 var, "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;
}