#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 ::/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 ::/Demo/Disk/DiskRaw.CC.
        //Do 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
RS_ATTR_READ_ONLY  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);
}