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 $LK,"Files Find",A="FI:::/Doc/FileUtils.DD"$ 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 $LK,"FUF_SCAN_PARENTS",A="MN: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","HC")	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;
}
