U8 *FileExtDot(U8 *src)
{//Find dot char in name.
        I64 ch;

        while (ch = *src++)
                if (ch == '.' && *src != '/' && *src != '.')
                        return src - 1;

        return NULL;
}

U8 *FileExtRemove(U8 *src, U8 *dst=NULL)
{//Remove filename extension from str.
        U8 *ptr;

        if (ptr = FileExtDot(src))
        {
                if (dst)
                        StrCopy(dst, ptr + 1);
                *ptr = 0;
        }
        else if (dst)
                *dst = 0;

        return dst;
}

Bool IsDotC(U8 *filename)
{//Does name end in .C?
        I64 i = StrLen(filename);

        if (StrOcc(filename, '.') > 1 && filename[i - 1] == 'C' && filename[i - 2] == '.')
                return TRUE;
        else
                return FALSE;
}

Bool FilesFindMatch(U8 *_test_name,U8 *files_find_mask,I64 fuf_flags=0)
{//Does filename meet Files Find mask?
        I64  tn_len = StrLen(_test_name), mask_len = StrLen(files_find_mask);
        U8      *mask1  = MAlloc(mask_len + 1), *mask2 = MAlloc(mask_len + 1), *ptr, *test_name1, *test_name2;
        Bool res    = FALSE;

        StrCopy(mask1,files_find_mask);
        if (StrOcc(_test_name, '/'))
        {
                test_name1 = MAlloc(tn_len + 1);
                test_name2 = MAlloc(tn_len + 1);
                StrCopy(test_name1, _test_name);
                StrLastRemove(test_name1, "/", test_name2);
        }
        else
        {
                test_name1 = NULL;
                test_name2 = NULL;
        }
        while (TRUE)
        {
                StrFirstRemove(mask1, ";", mask2);
                if (!test_name2 || StrOcc(mask2, '/'))
                        ptr = _test_name;
                else
                        ptr = test_name2;
                if (*mask2)
                {
                        if (*mask2 == '!')
                        {
                                if (WildMatch(ptr, mask2 + 1))
                                {
                                        res = FALSE;
                                        break;
                                }
                        }
                        else
                        {
                                if (WildMatch(ptr, mask2))
                                {
                                        if (Bt(&fuf_flags, FUf_JUST_TXT) && !FilesFindMatch(_test_name, FILEMASK_TXT)) {
                                                res = FALSE;
                                                break;
                                        }
                                        else if (Bt(&fuf_flags, FUf_JUST_DD) && !FilesFindMatch(_test_name, FILEMASK_DD)) {
                                                res = FALSE;
                                                break;
                                        }
                                        else if (Bt(&fuf_flags, FUf_JUST_SRC) && !FilesFindMatch(_test_name, FILEMASK_SRC)) {
                                                res = FALSE;
                                                break;
                                        }
                                        else if (Bt(&fuf_flags, FUf_JUST_AOT) && !FilesFindMatch(_test_name, FILEMASK_AOT)) {
                                                res = FALSE;
                                                break;
                                        }
                                        else if (Bt(&fuf_flags, FUf_JUST_JIT) && !FilesFindMatch(_test_name, FILEMASK_JIT)) {
                                                res = FALSE;
                                                break;
                                        }
                                        else if (Bt(&fuf_flags, FUf_JUST_GR) && !FilesFindMatch(_test_name, FILEMASK_GR)) {
                                                res = FALSE;
                                                break;
                                        }
                                        else
                                                res = TRUE;
                                }
                        }
                }
                else
                        break;
        }
        Free(test_name1);
        Free(test_name2);
        Free(mask1);
        Free(mask2);

        return res;
}

U8 *DirNameAbs(U8 *_dirname)
{//MAlloc absolute dir string with drv letter.
        I64 maxlen;
        U8  drv[3], *res, *buf, *buf2, *buf3, *buf4, *dirname, *free_dirname;

        if (!Fs->cur_dir || !*Fs->cur_dir)
                return StrNew(_dirname);
        free_dirname=dirname = MStrUtil(_dirname, SUF_REM_LEADING | SUF_REM_TRAILING | SUF_REM_CTRL_CHARS);
        *drv = Drive2Letter;
        drv[1] = ':';
        drv[2] = 0;
        if (*dirname && dirname[1] == ':')
        {
                if (*dirname == ':')
                        *drv = blkdev.boot_drive_let;
                else if (*dirname == '~')
                        *drv = *blkdev.home_dir;
                else
                        *drv = *dirname;
                dirname = dirname + 2;
                buf = StrNew("/");
        }
        else
                buf = StrNew(Fs->cur_dir);
        if (*dirname == '/')
        {
                Free(buf);
                buf = StrNew("/");
                dirname++;
        }
        buf2    = StrNew(dirname);
        maxlen  = StrLen(buf) + 1 + StrLen(buf2) + 1;
        buf3    = MAlloc(maxlen);
        buf4    = MAlloc(maxlen);
        StrCopy(buf3, buf);
        while (*buf2)
        {
                StrFirstRemove(buf2, "/", buf4);
                if (!*buf4)
                        StrCopy(buf3, "/");
                else if (!StrCompare(buf4, ".."))
                {
                        StrLastRemove(buf3, "/");
                        if (!*buf3)
                                StrCopy(buf3, "/");
                }
                else if (!StrCompare(buf4, "~"))
                {
                        Free(buf3);
                        buf3 = MAlloc(StrLen(blkdev.home_dir + 2) + 1 + StrLen(buf2) + 1);
                        StrCopy(buf3, blkdev.home_dir + 2);
                        *drv = *blkdev.home_dir;
                }
                else if (!StrCompare(buf4, "."));
                else if (*buf4)
                {
                        if (StrCompare(buf3, "/"))
                                CatPrint(buf3, "/");
                        CatPrint(buf3, buf4);
                }
        }
        Free(buf);
        res = MAlloc(StrLen(buf3) + 3);
        StrCopy(res, drv);
        StrCopy(res + 2, buf3);
        Free(buf2);
        Free(buf3);
        Free(buf4);
        Free(free_dirname);

        return res;
}

U8 *FileNameAbs(U8 *_filename, I64 fuf_flags=NONE)
{//Absolute filename. Accepts FUF_SCAN_PARENTS.
        U8                      *res, *filename, *buf, *buf_file, *buf_dir, *free_filename, *free_buf;
        CDirEntry        de;

        free_filename = filename = MStrUtil(_filename, SUF_REM_LEADING | SUF_REM_TRAILING | SUF_REM_CTRL_CHARS);
        free_buf = buf = StrNew(filename);
        if (*buf && buf[1] == ':')
        {
                buf += 2;
                filename += 2;
        }
        buf_file = MAlloc(StrLen(free_filename) + 1);
        StrLastRemove(buf, "/", buf_file);
        if (*filename == '/' && !*buf)
                StrCopy(buf, "/");
        buf_dir = DirNameAbs(free_buf);
        Free(free_buf);
        res = MAlloc(StrLen(buf_dir) + 1 + StrLen(buf_file) + 1);
        StrCopy(res, buf_dir);
        if (res[StrLen(res) - 1] != '/')
                CatPrint(res, "/");
        CatPrint(res, buf_file);
        Free(buf_file);
        Free(buf_dir);
        Free(free_filename);
        if (fuf_flags && FileFind(res, &de, fuf_flags | FUF_JUST_FILES))
        {
                Free(res);
                res = de.full_name;
        }

        return res;
}

U8 *ExtChange(U8 *filename, U8 *extension)
{//Change filename extension.
        U8 *res = MAlloc(StrLen(filename) + 1 + StrLen(extension) + 1);

        StrCopy(res, filename);
        if (FileExtDot(filename))
                FileExtRemove(res);

        return CatPrint(res, ".%s", extension);
}

U8 *ExtDefault(U8 *filename, U8 *extension)
{//Give extension if has none.
        U8 *res = MAlloc(StrLen(filename) + 1 + StrLen(extension) + 1);

        StrCopy(res, filename);
        if (!FileExtDot(filename))
                CatPrint(res, ".%s", extension);

        return res;
}

CDirEntry *Cd2DirEntry(CDirEntry *tmpde, U8 *abs_name)
{
        I64 i;

        while (tmpde)
        {
                i = StrLen(tmpde->full_name);
                if (StrNCompare(tmpde->full_name, abs_name, i) ||
                                i && tmpde->full_name[i - 1] != '/' &&
                                abs_name[i] &&
                                abs_name[i] != '/')
                        tmpde = tmpde->next;
                else
                        if (StrLen(abs_name) == i)
                                return tmpde;
                        else
                                return Cd2DirEntry(tmpde->sub, abs_name);
        }

        return NULL;
}

I64 FileAttr(U8 *name, I64 attr=0)
{
        if (IsDotC(name))
                attr |= RS_ATTR_CONTIGUOUS;

        return attr;
}

Bool FileNameCheck(U8 *filename)
{//Return check for valid filename, not checking existence.
        U8 *ptr = filename;

        if (!filename)
                return FALSE;
        if (!*ptr)
                return FALSE;
        if (*ptr == '.')
        {
                if (!ptr[1])
                        return TRUE;
                if (ptr[1] == '.' && !ptr[2])
                        return TRUE;
        }
        if (StrLen(filename) >= CDIR_FILENAME_LEN)
                return FALSE;
        while (*ptr)
                if (!Bt(char_bmp_filename, *ptr++))
                        return FALSE;

        return TRUE;
}

U8 *FileNameTmpTxt()
{//Make pretty-safe tmp filename in home dir.
        return MStrPrint("~/SysTmp%X.DD", TSCGet >> 8 & 0xFFFFFFFF);
}

U8 *DirCur(CTask *task=NULL, CTask *mem_task=NULL)
{//MAlloc copy of cur dir with drv letter.
        U8 *st;

        if (!task)
                task = Fs;
        if (!task->cur_dir)
                return NULL;
        st = MAlloc(StrLen(task->cur_dir) + 3, mem_task);
        *st = Drive2Letter(task->cur_dv);
        st[1] = ':';
        StrCopy(st + 2, task->cur_dir);

        return st;
}

U8 *DirFile(U8 *dirname, U8 *name=NULL, U8 *_extension=NULL)
{/*Strips file from dirname, scans for file upward until found or
returns default.

("/Kernel/KHashA.CC",NULL,NULL) returns "D:/Kernel"
("/Kernel",NULL,"PRJ")                                  returns "D:/Kernel/Kernel.PRJ"
("/Kernel/BlkDev",NULL,"PRJ")                                   returns "D:/Kernel/Kernel.PRJ"
("/Apps/Psalmody","Load","CC")  returns "D:/Apps/Psalmody/Load.CC"
*/
        U8 *st = DirNameAbs(dirname), *st2, *st3, *res, *default = NULL, *ext;

        if (_extension && *_extension)
        {
                if (*_extension == '.')
                        ext = StrNew(_extension);
                else
                        ext = MStrPrint(".%s", _extension);
        }
        else
                ext = StrNew("");
        while (StrOcc(st, '/') && !IsDir(st))
                StrLastRemove(st, "/");
        while (StrOcc(st, '/'))
        {
                st2 = StrNew(st);
                st3 = StrNew(st);
                StrLastRemove(st2, "/", st3);

                if (name)
                        res = MStrPrint("%s/%s%s", st, name, ext);
                else
                {
                        if (*ext)
                                res = MStrPrint("%s/%s%s", st, st3, ext);
                        else
                                res = StrNew(st);
                }
                if (!default)
                        default = StrNew(res);
                if (!*ext && (!name || !*name) || FileFind(res))
                {
                        Free(st3);
                        Free(st2);
                        Free(st);
                        Free(default);
                        Free(ext);
                        return res;
                }
                Free(st);
                st = st2;
                Free(st3);
        }
        Free(st);
        Free(ext);

        return default;
}