U0 HomeSet(U8 *dirname)
{//Change home directory.
	dirname = DirNameAbs(dirname);
	Free(blkdev.home_dir);
	blkdev.home_dir = ZStrNew(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. $LK,"Cd",A="MN: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;
}
