#help_index "File/Cmd Line (Typically);Cmd Line (Typically)"
public U8 *DBlk(I64 blk, Bool write=FALSE)
{//Dump disk block. Optionally, write.
//If you set write to TRUE, the block will
	//be written when you press <ESC>.
	//See $LK,"::/Demo/Disk/DiskRaw.CC"$.
	U8 *buf = MAlloc(BLK_SIZE);

	BlkRead(Fs->cur_dv, buf, blk, 1);
	DocD(buf, BLK_SIZE);
	if (write)
	{
		"Edit and press <ESC> to write or <SHIFT-ESC>\n";
		if (View)
		{
			"Write\n";
			BlkWrite(Fs->cur_dv, buf, blk, 1);
		}
	}

	return buf;
}

public U8 *DClus(I64 c, Bool write=FALSE, I64 num=0)
{//Dump disk clus. Optionally, write.
//If you set write to TRUE, the clus will
	//be written when you press <ESC>.
	//See $LK,"::/Demo/Disk/DiskRaw.CC"$.
	//Do $LK,"Dir",A="MN:Dir"$("*",TRUE); to get clus numbers of files.
	U8 *buf = MAlloc(Fs->cur_dv->spc << BLK_SIZE_BITS);

	c = ClusNumNext(Fs->cur_dv, c, num);
	ClusRead(Fs->cur_dv, buf, c, 1);
	"Clus:%X\n", c;
	DocD(buf, Fs->cur_dv->spc << BLK_SIZE_BITS);
	if (write)
	{
		"Edit and press <ESC> to write or <SHIFT-ESC>\n";
		if (View)
		{
			"Write\n";
			ClusWrite(Fs->cur_dv, buf, c, 1);
		}
	}

	return buf;
}

public U8 *Dump(U8 *filename, Bool write=FALSE)
{//Dump file. Optionally, write.
//If you set write to TRUE, the file will
	//be written when you press <ESC>.
	U8 *buf;
	I64 size;

	if (buf = FileRead(filename,&size))
	{
		DocD(buf, size);
		if (write)
		{
			"Edit and press <ESC> to write or <SHIFT-ESC>\n";
			if (View)
			{
				"Write\n";
				FileWrite(filename, buf, size);
			}
		}
	}

	return buf;
}

public Bool Copy(U8 *src_files_find_mask, U8 *dst_files_find_mask=".")
{//Copy files.
//If the name ends in ".Z", it will
	//be stored compressed.  If not ".Z"
	//it will be stored uncompressed.
	Bool		 res = TRUE;
	CDirContext	*dirc;
	CDirEntry	*tmpde, *tmpde1;
	U8			*st;

	if (!(tmpde1 = FilesFind(src_files_find_mask, FUF_CLUS_ORDER)))
		return FALSE;
	if (IsDir(dst_files_find_mask))
	{
		if (dirc = DirContextNew(dst_files_find_mask, TRUE))
		{
			tmpde = tmpde1;
			while (tmpde)
			{
				if (!(tmpde->attr & RS_ATTR_DIR))
				{
					st = FileNameAbs(tmpde->name);
					if (!CopySingle(tmpde->full_name, st))
						res = FALSE;
					Free(st);
				}
				tmpde = tmpde->next;
			}
			DirContextDel(dirc);
		}
		DirTreeDel(tmpde1);
		return res;
	}
	else
	{
		DirTreeDel(tmpde1);
		return CopySingle(src_files_find_mask, dst_files_find_mask);
	}
}

public Bool Move(U8 *f1, U8 *f2)
{//Move files from one location to another or rename.
	if (Copy(f1, f2))
	{
		Del(f1);
		return TRUE;
	}

	return FALSE;
}

I64 CopyTree2(CDirEntry *tmpde, I64 src_dir_len, I64 dst_dir_len, U8 *dst_dir)
{
	U8 *st;
	I64 res = 1;

	while (tmpde)
	{
		st = MAlloc(StrLen(tmpde->full_name) + dst_dir_len + 2);
		MemCopy(st, dst_dir, dst_dir_len);
		StrCopy(st + dst_dir_len, tmpde->full_name + src_dir_len);
		if (tmpde->attr & RS_ATTR_DIR)
		{
			DirMake(st, LinkedListCount(tmpde->sub));
			res += CopyTree2(tmpde->sub, src_dir_len, dst_dir_len, dst_dir);
		}
		else
			if (CopySingle(tmpde->full_name, st))
				res++;
		Free(st);
		tmpde = tmpde->next;
	}

	return res;
}
public I64 CopyTree(U8 *src_files_find_mask, U8 *dst_files_find_mask, Bool no_mask=TRUE)
{//Copy directory tree.
//Returns the count of copied files (not dirs).
	CDirContext	*dirc;
	CDirEntry	*tmpde = NULL;
	I64			 res = 0, i1, i2;
	U8			*st1, *st2;

	st1 = DirNameAbs(src_files_find_mask);
	st2 = DirNameAbs(dst_files_find_mask);
	i1 = StrLen(st1);
	if (!StrNCompare(st1, st2, i1) && (st2[i1] == '/' || !st2[i1]))
	{
		Free(st1);
		Free(st2);
		return 0;
	}
	Free(st1);
	Free(st2);
	if (dirc = DirContextNew(src_files_find_mask, TRUE,, no_mask))
	{
		tmpde = FilesFind(dirc->mask, FUF_RECURSE);
		st1 = DirCur;
		DirContextDel(dirc);
		i1 = StrLen(st1);
		if (i1 == 3)
			i1--;
		if (dirc = DirContextNew(dst_files_find_mask, TRUE, TRUE))
		{
			st2 = DirCur;
			i2 = StrLen(st2);
			if (i2 == 3)
				i2--;
			res = CopyTree2(tmpde, i1, i2, st2);
			DirContextDel(dirc);
			Free(st2);
		}
		DirTreeDel(tmpde);
		Free(st1);
	}

	return res;
}

I64 DelTreeDirs(CDirEntry *tmpde1)
{
	I64			 res = 0;
	CDirEntry	*tmpde2;

	while (tmpde1)
	{
		tmpde2 = tmpde1->next;
		if (tmpde1->attr & RS_ATTR_DIR)
		{
			if (tmpde1->sub)
				res += DelTreeDirs(tmpde1->sub);
			res += Del(tmpde1->full_name, TRUE, TRUE);
		}
		DirEntryDel(tmpde1);
		tmpde1 = tmpde2;
	}

	return res;
}
I64 DelTreeFiles(CDirEntry *tmpde1)
{
	I64			 res = 0;
	CDirEntry	*tmpde2;

	while (tmpde1)
	{
		tmpde2 = tmpde1->next;
		if (tmpde1->attr & RS_ATTR_DIR)
		{
			if (tmpde1->sub)
				res += DelTreeFiles(tmpde1->sub);
		}
		else
			res += Del(tmpde1->full_name, FALSE, TRUE);
		DirEntryDel(tmpde1);
		tmpde1 = tmpde2;
	}

	return res;
}
public I64 DelTree(U8 *files_find_mask, U8 *fu_flags=NULL)
{//Delete directory tree.
	I64 res = 0, fuf_flags = 0;

	FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), "+r");
	FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), fu_flags);
	if (IsDir(files_find_mask))
	{
		res = DelTreeDirs(FilesFind(files_find_mask, fuf_flags));
		res += Del(files_find_mask, TRUE, TRUE);
		res += Del(files_find_mask, FALSE, TRUE);
	}
	else
		res = DelTreeFiles(FilesFind(files_find_mask, fuf_flags));

	return res;
}

U0 TouchFile(U8 *filename, U8 *attr, CDate cdt=I64_MIN)
{
	CDrive		*drive = Letter2Drive(*filename);
	CDirEntry	 de;
	U8			*cur_dir = StrNew(filename), buf[STR_LEN];

	if (FileFind(filename, &de, FUF_JUST_FILES))
	{
		Free(de.full_name);
		if (!StrCompare(attr, "+?"))
			"%-48ts %s\n", filename, FlagsStrPrint(buf, Define("ST_FILE_ATTRS"), de.attr);
		else
		{
			StrFirstRemove(cur_dir, ":");
			StrLastRemove(cur_dir, "/");
			if (!*cur_dir)
				StrCopy(cur_dir, "/");
			FlagsScan(&de.attr, Define("ST_FILE_ATTRS"), attr);
			if (cdt == I64_MIN)
				de.datetime = Now;
			else
				de.datetime = cdt;
			DirNew(drive, cur_dir, &de, FALSE);
		}
	}
	else
		PrintErr("File not found: \"%s\".\n", filename);
	Free(cur_dir);
}
public U0 Touch(U8 *files_find_mask="*", U8 *attr="+?", U8 *fu_flags=NULL, CDate cdt=I64_MIN)
{/*Touch file attributes and DateTime.
Default lists attributes.
attr: "+?" =show current
"+T" =resident
$LK,"RS_ATTR_READ_ONLY",A="MN:RS_ATTR_READ_ONLY"$  $LK,"ST_FILE_ATTRS",A="MN:ST_FILE_ATTRS"$
To Set DateL:
Touch(filename,"",,datetime);
*/
	I64			 fuf_flags = 0;
	CDirEntry	*tmpde, *tmpde1;

	FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), "+f+F");
	FlagsScan(&fuf_flags, Define("ST_FILE_UTIL_FLAGS"), fu_flags);
	tmpde=tmpde1 = FilesFind(files_find_mask, fuf_flags);
	while (tmpde)
	{
		TouchFile(tmpde->full_name, attr, cdt);
		tmpde = tmpde->next;
	}
	DirTreeDel(tmpde1);
}
