U0 HomeSet(U8 *dirname)
{//Change home directory.
    dirname = DirNameAbs(dirname);
    Free(blkdev.home_dir);
    blkdev.home_dir = SysStrNew(dirname);
    Free(dirname);
}

Bool Cd(U8 *dirname=NULL, Bool make_dirs=FALSE)
{//Change directory. Optionally, make directories, too.
    I64      maxlen, cur_dir_clus = 0;
    U8      *chg_to_buf, *new_cur_dir, *buf;
    CDrive  *drive;
    Bool     res = TRUE;

    if (!dirname)
        dirname = "~";
    else if (!*dirname)
        return TRUE;
    if (dirname[1] == ':')
    {
        if (*dirname == ':')
        {
            if (Fs->cur_dv != Letter2Drive(':') && !Drive(*dirname))
                return FALSE;
        }
        else if (Fs->cur_dv != Letter2Drive(*dirname) && !Drive(*dirname))
            return FALSE;
        dirname += 2;
    }
    if (*dirname == '/' || !*dirname || !Fs->cur_dir)
    {
        Free(Fs->cur_dir);
        Fs->cur_dir = StrNew("/");
        if (*dirname == '/')
            dirname++;
    }
    chg_to_buf = MStrUtil(dirname, SUF_REM_LEADING | SUF_REM_TRAILING | SUF_REM_CTRL_CHARS);
    maxlen = StrLen(Fs->cur_dir) + 1 + StrLen(chg_to_buf) + 1;
    new_cur_dir = MAlloc(maxlen);
    buf = MAlloc(maxlen);
    StrCopy(new_cur_dir, Fs->cur_dir);
    while (*chg_to_buf && res)
    {
        StrFirstRemove(chg_to_buf, "/", buf);
        if (!*buf)
            StrCopy(new_cur_dir, "/");
        else if (!StrCompare(buf, ".."))
        {
            StrLastRemove(new_cur_dir, "/");
            if (!*new_cur_dir)
                StrCopy(new_cur_dir, "/");
        }
        else if (!StrCompare(buf, "~"))
        {
            Free(new_cur_dir);
            new_cur_dir = MAlloc(StrLen(blkdev.home_dir + 2) + 1 + StrLen(chg_to_buf) + 1);
            StrCopy(new_cur_dir, blkdev.home_dir + 2);
            if (Fs->cur_dv != Letter2Drive('~') && !Drive('~'))
                return FALSE;
        }
        else if (StrCompare(buf, ".") && *buf)
        {
            drive = Fs->cur_dv;
            cur_dir_clus = Name2DirClus(drive, new_cur_dir);
            switch (drive->fs_type)
            {
                case FSt_REDSEA:
                    res = RedSeaCd(buf, cur_dir_clus);
                    break;

                case FSt_FAT32:
                    res = FAT32Cd(buf, cur_dir_clus);
                    break;

                default:
                    PrintErr("File System Not Supported\n");
                    res = FALSE;
            }
            if (!res && make_dirs)
            {
                Free(Fs->cur_dir);
                Fs->cur_dir = StrNew(new_cur_dir);
                res = DirMake(buf);
            }
            if (res)
            {
                if (StrCompare(new_cur_dir, "/"))
                    CatPrint(new_cur_dir, "/");
                CatPrint(new_cur_dir, buf);
            }
        }
    }
    Free(Fs->cur_dir);
    Fs->cur_dir = StrNew(new_cur_dir);
    Free(buf);
    Free(chg_to_buf);
    Free(new_cur_dir);
    return res;
}

Bool IsDir(U8 *dir_name)
{//Is a str a valid, existing Dir?
    U8          *mask = MStrPrint("%s/*", dir_name);
    Bool         res, old_silent = Silent;
    CDirContext *dirc;

    if (dirc = DirContextNew(mask))
    {
        DirContextDel(dirc);
        res = TRUE;
    }
    else
        res = FALSE;
    Free(mask);
    Silent(old_silent);
    return res;
}

I64 Dir(U8 *files_find_mask, Bool full)
{//List directory.
    CDirEntry   *tmpde1 = NULL, *tmpde2;
    U8          *st;
    CDateStruct  ds;
    I64          csize = 0xFFFF, c = 0xFFFF, res = 0;

    tmpde1 = FilesFind(files_find_mask);
    if (!(st = DirCur))
        PrintErr("Invalid Drive\n");
    else
    {
        if (tmpde1)
        {
            Free(st);
            st = MAllocIdent(tmpde1->full_name);
            StrLastRemove(st, "/");
            if (!st[2])
                StrCopy(st + 2, "/");
//Find max columns
            tmpde2 = tmpde1;
            while (tmpde2)
            {
                if (tmpde2->size > csize)
                    csize = tmpde2->size;
                if (tmpde2->clus > c)
                    c = tmpde2->clus;
                tmpde2 = tmpde2->next;
            }
            csize = Bsr(csize) / 4 + 1;
            c = Bsr(c) / 4 + 1;

            "$MA,T=\"Directory\",LM=\"PopUpCd;Dir;\n\"$ of %s\n", st;
            if (full)
                "__DATE__ __TIME__ %*ts %*ts\n", csize, "SIZE", c, "BLK";
            else
                "DATE_ TIME_ %*ts\n", csize, "SIZE";
            while (tmpde1)
            {
                tmpde2 = tmpde1->next;
                res++;
                if (full)
                    "%D %T %0*tX %0*tX ", tmpde1->datetime, tmpde1->datetime, csize, tmpde1->size, c, tmpde1->clus;
                else
                {
                    Date2Struct(&ds, tmpde1->datetime + local_time_offset);
                    "%02d/%02d %02d:%02d %0*tX ", ds.mon, ds.day_of_mon, ds.hour, ds.min, csize, tmpde1->size;
                }
                if (tmpde1->attr & RS_ATTR_DIR)
                    PutDirLink(tmpde1->name, tmpde1->full_name);
                else
                    PutFileLink(tmpde1->name, tmpde1->full_name);
                '\n';
                DirEntryDel(tmpde1);
                tmpde1 = tmpde2;
            }
        }
        else
            "No matching entries\n";
        Free(st);
    }
    return res;
}

Bool DirMake(U8 *filename, I64 entry_count=0)
{//Make directory. Cd() can also make directories.
//entry_count is for preallocating dir blks, leave it zero if you like.
    U8          *name;
    CDirContext *dirc;
    Bool         res = FALSE;

    if (FileFind(filename,, FUF_JUST_DIRS))
        return FALSE;
    if (dirc = DirContextNew(filename))
    {
        if (*dirc->mask)
        {
            if (!FileNameCheck(dirc->mask))
                PrintErr("Invalid FileName: \"%s\".\n", dirc->mask);
            else
            {
                "Make Directory:%s\n", filename;
                name = MStrUtil(dirc->mask, SUF_REM_LEADING | SUF_REM_TRAILING | SUF_REM_CTRL_CHARS);
                switch (dirc->drive->fs_type)
                {
                    case FSt_REDSEA:
                        res = RedSeaMkDir(dirc->drive, Fs->cur_dir, name, entry_count);
                        break;

                    case FSt_FAT32:
                        res = FAT32MkDir(dirc->drive, Fs->cur_dir, name, entry_count);
                        break;

                    default:
                        PrintErr("File System Not Supported\n");
                }
                Free(name);
            }
        }
        DirContextDel(dirc);
    }
    return res;
}