#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,cnt=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)) {
          cnt++;
          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 cnt;
}

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 cnt=0,fuf_flags=0;
  CDirEntry *tmpde,*tmpde1;
  ScanFlags(&fuf_flags,Define("ST_FILE_UTIL_FLAGS"),"+r+i+f+F+T");
  ScanFlags(&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');
  LBEqu(&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)) {
    cnt+=FindFile(needle_str,tmpde->full_name,&fuf_flags,replace_text);
    tmpde=tmpde->next;
  }
  DirTreeDel(tmpde1);
  return cnt;
}

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 cnt=0;
  Bool old_silent=Silent(TRUE);
  cnt=Find(needle_str,files_find_mask,fu_flags);
  Silent(old_silent);
  return cnt;
}

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;
  StrCpy(g->filemask,FILEMASK_TXT);
  if (doc=DocPut) {
    StrCpy(g->find_text,doc->find_replace->find_text);
    StrCpy(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) {
      StrCpy(doc->find_replace->find_text,g->find_text);
      StrCpy(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;
}