#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 $LK,"FileFind",A="MN: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", "/Zenith/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;
}
