《计算机操作系统》大作业【参考代码解读】

注:前言、目录见 https://blog.csdn.net/qq_44220418/article/details/108428971

零、前言

这是大三(上)学期课程《计算机操作系统》的上机大作业,也是算是上机实验报告

惭愧的是,上机的前几节课都在观察文档、观察代码,也没记录点什么,到了最后写的时候,甚至连要求都没看清楚

要自己参考【参考代码】去实现fc、replace、move、batch等命令,并完善一些命令

因为大三(上)学期开学前几个月真的太浪太飘了,平时80%的课都没听、没跟着上,期末复习那两周天天预习一门考一门,时间太紧张了,我就选择直接拿【参考代码】进行理解作为我的上机作业了

我就一边对照着【参考代码】进行理解,一边用Typora编辑器记下笔记,从晚上23点,整理到第二天12点半,中间就睡了一个半小时,把整个代码理解了一遍,就有了下面的这些笔记

去老师那汇报的时候,老师还是指出了我几个必做的要求没做,我也坦白了我是直接拿的【参考代码】进行了理解、上面的我都能理解,结果还是被老师稍微批评了一下,老师说他是希望同学能自己写、不这么逼出来哪能培养出什么能力

好在在跟老师解释代码的时候,有些小的代码细节的还是能理解,老师也吐槽我“编程基础还不错,为啥不自己写”,我就实话实说了“期末太多课要复习实在没时间”,当时还是挺慌的😰😰😰

不过既然好不容易整理出来了这么一份笔记,就发到博客上分享一下了,说不定还能对后人有点帮助😳😳😳

下面另附【老师提供的参考代码的我的微改版】和【实验指导书】


链接:https://pan.baidu.com/s/1yO3AcVWEJCUemt255mu3cg
提取码:5257

一、类型定义

CurPath

解释

当前目录的数据结构,包括 { 首 块 号 路 径 字 符 串 \begin{cases}首块号 \\ 路径字符串 \end{cases} {

原型

struct CurPath		//定义存储当前目录的数据结构
{
	short int fblock;			//当前目录的首块号
	char cpath[PATH_LEN];		//当前目录绝对路径字符串(全路径名)
};

FCB

解释

文件目录项,包括 { 文 件 名 2 ∼ 11 字 节 ( 包 括 ′ \ 0 ′ ) 文 件 属 性 1 字 节 { 0 表 示 文 件 16 表 示 目 录 其 他 详 见 文 件 属 性 图 文 件 首 块 号 2 字 节 文 件 长 度 2 字 节 \begin{cases}文件名 & 2 \sim 11字节(包括'\backslash0')\\ \\ 文件属性 & 1字节\begin{cases}0 & 表示文件 \\ 16 & 表示目录 \\ 其他 & 详见文件属性图 \end{cases}\\ \\ 文件首块号 & 2字节\\ \\ 文件长度 & 2字节 \end{cases} 211\0101622

文件属性如下图

image-20201127160139367

原型

struct FCB			//定义文件目录项FCB的结构(共16个字节)
{
	char FileName[FILENAME_LEN];//文件名(1~10字节)
	char Fattrib;				//文件属性
	short int Addr;				//文件首块号
	short int Fsize;			//文件长度
};

UnDel

解释

恢复被删除文件表的数据结构,包括 { 被 删 除 文 件 的 全 路 径 名 ( 不 含 文 件 名 ) 被 删 除 文 件 的 文 件 名 被 删 除 文 件 的 首 块 号 存 储 被 删 除 文 件 块 号 的 第 一 个 块 号 \begin{cases}被删除文件的全路径名(不含文件名)\\ 被删除文件的文件名\\ 被删除文件的首块号\\ 存储被删除文件块号的第一个块号\\ \end{cases}

原型

struct UnDel		//恢复被删除文件表的数据结构
{
	char gpath[PATH_LEN];		//被删除文件的全路径名(不含文件名)
	char ufname[FILENAME_LEN];	//被删除文件名
	short ufaddr;				//被删除文件名的首块号
	short fb;					//存储被删除文件块号的第一个块号(链表头指针)
								//首块号也存于fb所指的盘块中
};

UOF

解释

用户打开文件表的数据结构 { 文 件 全 路 径 名 文 件 属 性 { 0 读 写 1 只 读 文 件 的 首 块 号 文 件 大 小 ( 字 节 数 ) 目 录 项 指 针 打 开 状 态 { 0 空 表 项 1 新 建 2 打 开 读 指 针 { 0 空 文 件 其 他 指 向 要 读 写 的 字 符 位 置 读 写 指 针 \begin{cases}文件全路径名\\ \\ 文件属性 & \begin{cases}0 & 读写\\ 1 & 只读\end{cases}\\ \\ 文件的首块号\\ \\ 文件大小(字节数)\\ \\ 目录项指针\\ \\ 打开状态 & \begin{cases}0 & 空表项\\ 1 & 新建\\ 2 & 打开\end{cases}\\ \\ 读指针 & \begin{cases}0 & 空文件\\ 其他 & 指向要读写的字符位置\end{cases}\\ \\ 读写指针\\ \end{cases} {01012{0

原型

struct UOF			//定义用户打开文件表的结构
{
	char fname[PATH_LEN];		//文件全路径名
	char attr;					//文件属性,1=只可读;0=可读写
	short int faddr;			//文件的首块号
	short int fsize;			//文件大小(字节数)
	FCB *fp;					//该文件的目录项指针
	short int state;			//状态:0=空表项;1=新建;2=打开
	short int readp;			//读指针,指向某个要读的字符位置,0=空文件
	short int writep;			//写读指针,指向某个要写读的字符位置
};

二、全局变量

curpath

解释

存储当前路径的信息,包括首块号和路径字符串

原型

CurPath curpath;

FAT[K]

解释

FAT表,盘块数为K

  • FAT[0]用来存储空闲盘块数
  • FAT[i] = m意味着一个文件目录,占用到i号盘块,并且在i号盘块占用完后将继续占用m号盘块
  • FAT[i] = -1意味着一个文件目录,最后最多占到i号盘块

下面是百度百科上的定义:

文件分配表FAT(File Allocation Table)用来描述文件系统内存储单元的分配状态及文件内容的前后链接关系的表格(也可以笼统的说成是记录文件所在位置的表格)

原型

int FAT[K];

Disk[K][SIZE]

解释

定义磁盘空间,有K个盘块,每个盘块容量为SIZE个字节

在本次实验的系统中

  • 1个Disk中包含了4个FCB,即1个磁盘中包含4个文件目录项
  • Disk[1]~Disk[30]表示根目录,因此根目录最多可以容纳120个文件目录项
  • Disk[i]代表第i个盘块,每个盘块最多存放SIZE / sizeof(FCB)(本次实验是4)个目录项

原型

char Disk[K][SIZE];

udtab[DM]

解释

存储系统的被删除文件恢复表,最多存储DM个被删除文件的信息(超出时保留最新的DM个)

  • 一个文件被恢复后,将其在udtab中的信息删除

  • 退出系统时,该表可存入文件UdTab.dat文件中

原型

UnDel udtab[DM];

Udelp

解释

标识被删除文件恢复表udtab的第一个空表项的下标

  • Udelp = 0代表udtab为空
  • Udelp = DM代表udtab为已满
  • 系统退出时,其值保存到0号盘块Disk[0]

原型

short Udelp;

ffbp

解释

标识分配盘块时FAT表的起始下标,将从该位置开始查找空闲盘块(类似循环首次适应分配

  • 系统退出时,其值保存到0号盘块Disk[0]

原型

short ffbp;

uof[S]

解释

用户打开文件表,存放用户打开文件的打开文件信息

  • 最多存放S个打开文件信息,最多同时打开S个文件

原型

UOF uof[S];

comd[CK][PATH_LEN]

解释

暂存命令分析结果(命令-参数表)

  • 命令最多可以被分解为CK
  • 每段命令名、命令参数最长为PATH_LEN

原型

char comd[CK][PATH_LEN];

三、函数定义

1、主流程控制函数

ParseCommand

解释

将用户输入的命令语句根据空格分隔,划分出命令、参数,存入comd[i]

  • 命令存入comd[0]
  • 参数按序存入comd[1]comd[2]……

最后对分析出的命令-参数表中的命令comd[0]进行特殊处理

  • 因为允许出现诸如cd/bin这样的命令,所以需要将路径参数从comd[0]中截取到comd[1]
  • 截取前应当将之前分析好的其他参数comd[1]comd[2]等后移到comd[2]comd[3]

参数

参数原型说明
char* p用户输入的命令字符串

返回值

返回值说明
int k分析出命令字符串的参数个数

定义

int ParseCommand(char *p)	//将输入的命令行分解成命令和参数等
{
	int i, j, k, g = 0;
	for (i = 0; i < CK; i++)					//初始化comd[][] 
		comd[i][0] = '\0';
	for (k = 0; k < CK; k++)
	{	//分解命令及其参数,comd[0]中是命令,comd[1],comd[2]...是参数
		for (i = 0; *p != '\0'; i++, p++)
			if (*p != ' ')				//空格是命令、参数之间的分隔符
				comd[k][i] = *p;		//取命令标识符
			else
			{
				comd[k][i] = '\0';
				if (strlen(comd[k]) == 0) k--;
				p++;
				break;
			}
		if (*p == '\0')
		{
			comd[k][i] = *p;
			break;
		}
	}
	for (i = 0; comd[0][i] != '\0'; i++)
		if (comd[0][i] == '/')		//处理cd/,dir/usr等情况
			break;
	if (comd[0][i] != '\0')			//comd[0]中存在字符'/'
	{
		if (k > 0)
			for (j = k; j > 0; j--)
				strcpy(comd[j + 1], comd[j]);	//后移
		strcpy(comd[1], &comd[0][i]);
		comd[0][i] = '\0';
		k++;	//多出一个参数
	}
	return k;
}

ExecComd

解释

操作流程如下:

  • 根据comd[0]的命令字符串,和命令表CmdTab中的各项逐个进行不区分大小写的比较,用cid记录匹配的命令的索引
  • 通过switch语句判断cid对应哪个命令,执行相应命令(不存在则提示命令错误)

参数

参数原型说明
int k命令的参数个数

返回值

返回值说明
void

定义

void ExecComd(int k)		//执行命令
{
	int cid;				//命令标识

	//操作命令表 
	char CmdTab[][COMMAND_LEN] = { "create","open","write","read","close",
		"del","dir","cd","md","rd","ren","copy","type","help","attrib",
		"uof","closeall","block","rewind","fseek","fat","check","exit",
		"undel","Prompt","udtab" };
	int M = sizeof(CmdTab) / COMMAND_LEN;	//统计命令个数
	for (cid = 0; cid < M; cid++)			//在命令表中检索命令
		if (_stricmp(CmdTab[cid], comd[0]) == 0)//命令不区分大小写
			break;
	//以下命令函数中,命令参数通过全局变量comd[][]传递,故未出现在函数参数表中
	switch (cid)
	{
	case 0:CreateComd(k);		//create命令,建立文件
		break;
	case 1:OpenComd(k);		//open命令,打开文件 
		break;
	case 2:WriteComd(k);		//write命令,k为命令中的参数个数(命令本身除外) 
		break;
	case 3:ReadComd(k);		//read命令,读文件 
		break;
	case 4:CloseComd(k);		//close命令,关闭文件 
		break;
	case 5:DelComd(k);			//del命令,删除文件
		break;
	case 6:DirComd(k);			//dir命令 
		break;
	case 7:CdComd(k);			//cd命令 
		break;
	case 8:MdComd(k);			//md命令 
		break;
	case 9:RdComd(k);			//rd命令 
		break;
	case 10:RenComd(k);		//ren命令,文件更名 
		break;
	case 11:CopyComd(k);		//copy命令,复制文件
		break;
	case 12:TypeComd(k);		//type命令,显示文件内容(块号) 
		break;
	case 13:HelpComd();		//help命令,帮助信息 
		break;
	case 14:AttribComd(k);		//attrib命令,修改文件属性 
		break;
	case 15:UofComd();			//uof命令,显示用户的UOF(文件打开表) 
		break;
	case 16:CloseallComd(1);	//closeall命令,关闭所有文件
		break;
	case 17:blockf(k);			//block命令,显示文件的盘块号
		break;
	case 18:RewindComd(k);		//rewind命令,将读指针移到文件开头 
		break;
	case 19:FseekComd(k);		//fseek命令:将读、写指针都移到指定记录号 
		break;
	case 20:FatComd();			//fat命令
		break;
	case 21:CheckComd();		//check命令
		break;
	case 22:ExitComd();		//exit命令
		break;
	case 23:UndelComd(k);		//undel命令
		break;
	case 24:PromptComd();		//prompt命令
		break;
	case 25:UdTabComd();		//udtab命令
		break;
	default:cout << "\n命令错:" << comd[0] << endl;
	}
}

2、命令操作函数

CreateComd

解释

根据命令、参数创建文件,即在目录中增加一个目录项

注意点如下:

  • create命令只能有1~2个参数

操作流程如下:

  • 判断参数个数,如果不满足1~2个参数,提示命令中参数个数不对并返回-1
  • 通过ProcessPath函数,查找新文件路径,并将文件名写入变量FileName中,需要的参数个数可任意,文件属性为'\020'(这是8进制的字符表示方法,下面不再重复),代表要查找目录项是目录;调用返回成功值后,全局变量temppath将存储新文件路径中直到最后一级目录的路径
  • 通过IsName函数检验文件名是否合规,不合规则提示命令中的新文件名错误并返回-2
  • 通过FindFCB函数查找盘块s下是否存在名为FileName的目录项,存在则提示有同名文件,不能建立并返回-2
  • 根据temppathFileName构造全路径名到gFilePath
  • 如果命令参数个数为2,则需要设置文件属性(默认值为'\000'表示文件),将文件属性字符串逐字符转换为属性attrib上各位的0 / 1值(通过不断按位或运算);若文件属性有误,则提示输入的文件属性错误并返回-3
  • uof表中寻找空表项,用i_uof标记空表项的索引号;若uof表满则提示UOF已满,不能创建文件并返回-4
  • 通过FindBlankFCB函数尝试在盘块s所链接的盘块链(已满时尝试扩充盘块链)中找到一个空目录项,若找不到则提示创建文件失败并返回-5
  • 通过空目录项指针fcbp1设置新的空文件的相关属性,主要为文件属性,其余属性设置为空文件属性
  • 通过Put_UOF函数将新建的文件登记到uof表中,提示文件XXX建立成功并返回1

命令

命令格式命令说明
create <文件名> [<文件属性>]创建一个指定名字的新文件

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1参数个数错误
-2新文件的文件名名不合法 / 已有同名文件
-3新文件的文件属性有误
-4uof表已满,不能创建新文件
-5无法找到 / 创建空目录项
1新建文件成功

定义

int CreateComd(int k)		//create命令处理函数:建立新文件 
{
	// 创建文件:create <文件名> [<文件属性>],创建一个指定名字的新文件,
	// 即在目录中增加一目录项,不考虑文件的内容。对于重名文件给出错误信息。

	short i, i_uof, s0, s;
	char attrib = '\0', *FileName;
	char gFileName[PATH_LEN];	//存放文件全路径名
	char ch, *p;
	FCB* fcbp1;
	if (k > 2 || k < 1)
	{
		cout << "\n命令中参数个数不对。\n";
		return -1;
	}
	s = ProcessPath(comd[1], FileName, k, 0, '\020');//取FileName所在目录的首块号
	if (s < 1)			//路径错误
		return s;		//失败,返回
	if (!IsName(FileName))		//若名字不符合规则
	{
		cout << "\n命令中的新文件名错误。\n";
		return -2;
	}
	s0 = FindFCB(FileName, s, attrib, fcbp1);	//取FileName的首块号(查其存在性)
	if (s0 > 0)
	{
		cout << "\n有同名文件,不能建立。\n";
		return -2;
	}
	strcpy(gFileName, temppath);
	i = strlen(temppath);
	if (temppath[i - 1] != '/')
		strcat(gFileName, "/");
	strcat(gFileName, FileName);	//构造文件的全路径名
	if (k == 2)
	{
		p = comd[2];
		while (*p != '\0')	//处理文件属性
		{
			ch = *p;
			ch = tolower(ch);
			switch (ch)
			{
			case 'r': attrib = attrib | (char)1;
				break;
			case 'h': attrib = attrib | (char)2;
				break;
			case 's': attrib = attrib | (char)4;
				break;
			default: cout << "\n输入的文件属性错误。\n";
				return -3;
			}
			p++;
		}
	}
	for (i_uof = 0; i_uof < S; i_uof++)			//在UOF中找空表项
		if (uof[i_uof].state == 0)
			break;
	if (i_uof == S)
	{
		cout << "\nUOF已满,不能创建文件。\n";
		return -4;
	}
	i = FindBlankFCB(s, fcbp1);			//寻找首块号为s的目录中的空目录项
	if (i < 0)
	{
		cout << "\n创建文件失败。\n";
		return i;
	}
	strcpy(fcbp1->FileName, FileName);	//目录项中保存文件名
	fcbp1->Fattrib = attrib;				//复制文件属性
	fcbp1->Addr = 0;						//空文件首块号设为0
	fcbp1->Fsize = 0;						//空文件长度为0
	Put_UOF(gFileName, i_uof, 1, fcbp1);	//建立UOF登记项
	cout << "\n文件" << gFileName << "建立成功\n";
	return 1;							//文件创建成功,返回
}

OpenComd

解释

根据命令、参数打开文件,并在uof表登记

注意点如下:

  • open命令只能有1个参数

操作流程如下:

  • 通过ProcessPath函数取得comd[1]路径下文件所在目录的首块号s0,如果路径错误,返回s0
  • 通过FindFCB函数查找盘块s下是否存在名为FileName的目录项,不存在则提示要打开的文件不存在并返回-2
  • 根据temppathFileName构造全路径名到gFilePath
  • 通过Check_UOF函数判断该文件是否已经打开:
    • 若已打开,则提示文件XXX原先已打开并返回-5
    • 若未打开,则尝试在uof表中寻找空表项:
      • uof已满,则提示UOF已满,不能打开文件并返回-4
      • uof未满,则通过Put_UOF函数将该文件放入uof表,提示文件XXX打开成功并返回1

命令

命令格式命令说明
open <文件名>打开指定文件

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2文件不存在
-3路径名有误
-4uof表已满
-5文件已打开
1文件打开成功

定义

int OpenComd(int k)			//open命令处理函数:打开文件 
{
	// 命令形式:open <文件名>
	// 若指定文件存在且尚未打开,则打开之,并在用户打开文件表(UOF)中登
	// 记该文件的有关信息。若指定文件已经打开,则显示"文件已打开"的信息;
	// 若指定文件不存在,则给出错误信息。只读文件打开后只能读不能写。

	short i, s0, s;
	char attrib = '\0', *FileName;
	char gFileName[PATH_LEN];	//存放文件全路径名
	FCB* fcbp;

	s0 = ProcessPath(comd[1], FileName, k, 1, '\20');//取FileName所在目录的首块号
	if (s0 < 1)			//路径错误
		return s0;		//失败,返回
	s = FindFCB(FileName, s0, attrib, fcbp);		//取FileName的首块号(查其存在性)
	if (s < 0)
	{
		cout << "\n要打开的文件不存在。\n";
		return -2;
	}
	strcpy(gFileName, temppath);
	i = strlen(temppath);
	if (temppath[i - 1] != '/')
		strcat(gFileName, "/");
	strcat(gFileName, FileName);	//构造文件的全路径名
	i = Check_UOF(gFileName);		//查UOF
	if (i < S)					//该文件已在UOF中
	{
		cout << "\n文件" << gFileName << "原先已经打开!\n";
		return -5;
	}
	for (i = 0; i < S; i++)			//在UOF中找空表项
		if (uof[i].state == 0)
			break;
	if (i == S)
	{
		cout << "\nUOF已满,不能打开文件。\n";
		return -4;
	}
	Put_UOF(gFileName, i, 2, fcbp);
	cout << "\n文件" << gFileName << "打开成功。\n";
	return 1;
}

WriteComd

解释

根据命令、参数写入文件

操作流程如下:

  • 若参数个数为0,则提示命令中没有文件名并返回-1
  • 通过Check_UOF函数查找uof表,若uof表中不存在,则提示未打开或不存在,不能写文件并返回-2
  • 通过uof表中相关表项判断文件的只读属性,若文件只读,则提示XXX是只读文件,不能写并返回-3
  • 分析参数,得到读写指针位置、读写模式
    • 若写入位置参数有误,则提示命令中提供的写入位置错误并返回-4
    • 若写入模式参数有误,则提示命令参数XXX XXX有误并返回-5
  • 提示用户输入,等待用户输入
    • 若输入为空,则不改变文件并返回0
    • 若输入不为空
      • 若原文件为空,通过buffer_to_file函数将用户输入的数据写入文件对应的盘块中
        • 写入失败,提示磁盘空间不足,不能将信息写入文件并返回-6
        • 写入成功,修改uof表中对应表项的相关信息,并返回1
      • 若原文件非空
        • 判断剩余盘块数是否满足所需盘块数,若不满足,则提示磁盘空间不足,不能将信息写入文件并返回-6
        • 动态为buf申请空间,若失败,则提示分配内存失败并返回-7
        • 通过file_to_buffer函数从原文件中读取数据到buf
        • 根据不同的读写模式,将用户输入的数据Buffer和原文件的数据buf生成一个最终数据存入Buffer,通过buffer_to_file函数将最终数据写入文件对应盘块,修改uof表中相关表项的信息,并返回1

命令

命令格式命令说明
write <文件名>在写指针当前所指位置写,写入内容代替原内容【代替方式 / 改写方式】
write <文件名> <n>在文件开头第n个字节处写【改写方式】
write <文件名> insert在写指针所指位置写,写入处开始的原内容后移【插入方式】
write <文件名> <n> insert在文件开头第n个字节处写【插入方式】
write <文件名> append在文件尾部写【添加方式】

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2文件未打开
-3只读文件,不能写入
-4读写位置参数有误
-5读写模式参数有误
-6磁盘空间不足
-7动态为buf申请空间失败
0文件未改变
1写入成功

定义

int WriteComd(int k)		//write命令的处理函数
{
	// 写文件:write <文件名> [<位置>[ insert]],命令中若无"位置"参数,则在写指
	// 针所指位置写入文件内容;若提供"位置"参数,则在对应位置写入内容。位置可以
	// 是整数n,是指在文件的第n个字节处开始写入(位置从1开始编号)。"位置" 还可以
	// 是 "append"(前3个字符有效,不区分大小写),表示在文件尾部写入信息;若有
	// 参数 "insert"(前3个字符有效,不区分大小写),则新写入的内容插入到对应位
	// 置,对应位置开始的原内容后移。若无参数 "insert" ,写入的内容代替文件原先
	// 的内容(对应位置的内容)。写入完毕调整文件长度和写指针值。
	// 若文件未打开或文件不存在,分别给出错误信息。

	// 可以有如下几种命令形式:
	//	write <文件名> ——在写指针当前所指位置写,写入内容代替原内容(代替方式或改写方式)
	//	write <文件名> <n>——在文件开头第n个字节处写,改写方式
	//	write <文件名> insert——在写指针所指位置写,写入处开始的原内容后移(插入方式)
	//	write <文件名> <n> insert——在文件开头第n个字节处写,插入方式
	//	write <文件名> append——在文件尾部写(添加方式)

	//【思考】如何使参数“insert”、“append”只要前3个字符对就可以,但多于3个字符也行。例如:
	// 对于“insert”,输入ins、inse、inser、insert(不区分大小写)都可以,输入其它不行。

#define BSIZE 40*SIZE+1
	short int ii, ii_uof, len0, len, len1, pos, ins = 0;
	short int bn0, bn1, jj, count = 0;
	char attrib = '\0', Buffer[BSIZE];		//为方便计,假设一次最多写入2560字节
	char *buf;
	FCB *fcbp;

	if (k < 1)
	{
		cout << "\n命令中没有文件名。\n";
		return -1;
	}
	FindPath(comd[1], attrib, 0, fcbp);	//构成全路径且去掉“..”存于temppath中
	ii_uof = Check_UOF(temppath);			//查UOF
	if (ii_uof == S)
	{
		cout << "\n文件" << temppath << "未打开或不存在,不能写文件。\n";
		return -2;
	}
	if (uof[ii_uof].attr&'\01' && uof[ii_uof].state != 1)
	{	//只读文件不是创建状态不能写
		cout << "\n" << temppath << "是只读文件,不能写。\n";
		return -3;
	}
	if (k == 1)
		pos = uof[ii_uof].writep;	//从写指针所指位置开始写(write <文件名>)
	else		//k=2或3
	{
		if (_strnicmp(comd[2], "app", 3) == 0)
			pos = uof[ii_uof].fsize + 1;	//文件尾部添加模式(write <文件名> append)
		else if (_strnicmp(comd[2], "ins", 3) == 0)
		{
			pos = uof[ii_uof].writep;	//从当前写指针位置开始写
			ins = 1;					//插入模式(write <文件名> insert)
		}
		else
		{
			pos = atoi(comd[2]);		//从命令中指定位置写(write <文件名> <n>)
			if (pos <= 0)
			{
				cout << "\n命令中提供的写入位置错误。\n";
				return -4;
			}
			if (k == 3)
			{
				if (_strnicmp(comd[3], "ins", 3) == 0)
					ins = 1;			//插入模式(write <文件名> <n> insert)
				else
				{
					cout << "\n命令参数" << comd[2] << "," << comd[3] << "错误\n";
					return -5;
				}
			}
		}
	}
	if (pos <= 0)
	{
		cout << "\n命令中提供的写入位置错误。\n";
		return -4;
	}
	if (pos >= uof[ii_uof].fsize + 1)
	{
		pos = uof[ii_uof].fsize + 1;
		ins = 0;						//这种情况不会是插入方式
	}

	pos--;							//使pos从0开始

	cout << "\n请输入写入文件的内容(最多允许输入" << sizeof(Buffer) - 1 << "个字节):\n";
	cin.getline(Buffer, BSIZE);
	len1 = strlen(Buffer);
	if (len1 == 0)			//输入长度为0,不改变文件
		return 0;
	fcbp = uof[ii_uof].fp;
	len0 = uof[ii_uof].fsize;				//取文件原来的长度值
	if (len0 == 0)						//若是空文件
	{
		ii = buffer_to_file(fcbp, Buffer);
		if (ii == 0)	//写文件失败
			return -6;
		uof[ii_uof].fsize = uof[ii_uof].fp->Fsize;
		uof[ii_uof].faddr = uof[ii_uof].fp->Addr;
		uof[ii_uof].readp = 1;
		uof[ii_uof].writep = uof[ii_uof].fsize + 1;
		return 1;
	}
	//以下处理文件非空的情况
	len = len1 + pos + ins * (len0 - pos);		//计算写入完成后文件的长度
	bn0 = len0 / SIZE + (short)(len0%SIZE > 0);	//文件原来占用的盘块数
	bn1 = len / SIZE + (short)(len%SIZE > 0);		//写入后文件将占用的盘块数
	if (FAT[0] < bn1 - bn0)
	{
		cout << "\n磁盘空间不足,不能写入文件.\n";
		return -6;
	}
	buf = new char[len + 1];
	if (buf == 0)
	{
		cout << "\n分配内存失败。\n";
		return -7;
	}
	file_to_buffer(fcbp, buf);		//文件读到buf
	if (ins)	//若是插入方式
	{
		for (ii = len0; ii >= pos; ii--)
			buf[ii + len1] = buf[ii];	//后移,空出后插入Buffer
		jj = pos;
		ii = 0;
		while (Buffer[ii] != '\0')		//Buffer插入到buf
			buf[jj++] = Buffer[ii++];
	}
	else		//若是改写方式
		strcpy(&buf[pos], Buffer);
	buffer_to_file(fcbp, buf);
	delete[] buf;
	uof[ii_uof].fsize = uof[ii_uof].fp->Fsize;
	uof[ii_uof].writep = uof[ii_uof].fsize + 1;
	cout << "\n写文件" << uof[ii_uof].fname << "成功.\n";
	return 1;
}

ReadComd

解释

根据命令、参数读取文件

操作流程如下:

  • 判断参数个数,若参数个数有误,则提示命令中参数个数太多或太少并返回-1
  • 通过FindPath函数将相对路径解析构成全路径,存入全局变量temppath
  • 通过Check_UOF函数检查文件是否打开,若未打开,则提示文件XXX未打开或不存在,不能读文件并返回-2
  • 若文件的读指针位置为0,则代表文件为空文件,提示文件XXX是空文件并返回1
  • 准备好要读取的字节数readc,这需要根据不同的参赛个数进行不同的错误处理:
    • 参数个数为1,若读指针已指向文末,则提示读指针已指向文件尾部,无可读信息并返回1
    • 参数个数为2~3
      • 若读指针位置参数在开始前或文末后,则提示命令中提供的读位置错误并返回-3
      • 若读指针大小参数小于1,则提示命令中提供的读字节数错误并返回-4
  • 根据pos计算读取的起始盘块号b、块内读位置offset
  • s盘块的offset位置开始,往s盘块向后的链接表中的各个盘块,读取数据到Buffer直到长度为readc
  • 读取完成后,调整uof表中相应表项的读指针位置,返回1

命令

命令格式命令说明
read <文件名>从读指针开始读文件,一直读到文件末尾为止
read <文件名> <位置m>从文件第m个字节开始,一直读到文件末尾为止
read <文件名> <位置m> <字节数n>从文件第m个字节开始,共读n个字节

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2文件未打开
-3位置参数错误
-4大小参数错误
1读取完成

定义

int ReadComd(int k)		//read命令的处理函数:读文件 
{
	// 读文件:read <文件名> [<位置m> [<字节数n>],从已打开的文件读文件内容并显示。若无
	// “位置”参数,则从读指针所指位置开始读。若有"位置"参数,则从指定位置处开始读。位
	// 置m是指从文件开头第m个字节处读(m从1开始编号)。若无"字节数"参数,则从指定位置读
	// 到文件末尾;若有"字节数n"参数,则从指定位置开始读n个字节。每读一个字节,读指针后
	// 移一个字节。若文件未打开或文件不存在,分别给出错误信息。
	// read命令有如下几种形式:
	//		read <文件名>——从读指针开始读文件,一直读到文件末尾为止。
	//		read <文件名> <位置m>——从文件第m个字节开始,一直读到文件末尾为止。
	//		read <文件名> <位置m> <字节数n>>——从文件第m个字节开始,共读n个字节。
	// 说明:刚打开的文件,其读指针指向文件开头(即读指针等于1),约定空文件的读指针等于0。

	short i, j, ii, i_uof, pos, offset;
	short b, b0, bnum, count = 0, readc;
	char attrib = '\0';
	char Buffer[SIZE + 1];
	FCB* fcbp;

	if (k < 1 || k>3)
	{
		cout << "\n命令中参数个数太多或太少。\n";
		return -1;
	}
	FindPath(comd[1], attrib, 0, fcbp);	//构成全路径且去掉“..”存于temppath中
	i_uof = Check_UOF(temppath);			//查UOF
	if (i_uof == S)
	{
		cout << "\n文件" << temppath << "未打开或不存在,不能读文件。\n";
		return -2;
	}
	if (uof[i_uof].readp == 0)
	{
		cout << "\n文件" << temppath << "是空文件。\n";
		return 1;
	}
	if (k == 1)				//参数个数k=1的情况(无参数m和n)
	{
		pos = uof[i_uof].readp;//从读指针所指位置开始读
		if (pos > uof[i_uof].fsize)
		{
			cout << "\n读指针已指向文件尾部,无可读信息。\n";
			return 1;
		}
		readc = uof[i_uof].fsize - pos + 1;	//读到文件尾部共需读readc个字节
	}
	else					//k=2或k=3的情况
	{
		pos = atoi(comd[2]);		//从命令中指定位置写
		if (pos <= 0 || pos > uof[i_uof].fsize)
		{
			cout << "\n命令中提供的读位置错误。\n";
			return -3;
		}
		readc = uof[i_uof].fsize - pos + 1;	//读到文件尾部共需读readc个字节
		if (k == 3)
		{
			readc = atoi(comd[3]);
			if (readc < 1)
			{
				cout << "\n命令中提供的读字节数错误。\n";
				return -4;
			}
			if (readc > uof[i_uof].fsize - pos + 1)
				readc = uof[i_uof].fsize - pos + 1;
		}
	}
	bnum = (pos - 1) / SIZE;		//从文件的第bnum块读(bnum从0开始编号)
	offset = (pos - 1) % SIZE;	//在第bnum块的偏移位置offset处开始读(offset从0开始)
	b = uof[i_uof].faddr;		//取文件首块号
	for (i = 0; i < bnum; i++)	//寻找读入的第一个盘块号
	{
		b0 = b;
		b = FAT[b];
	}
	ii = offset;
	while (count < readc)		//读文件至Buffer并显示之
	{
		for (i = ii, j = 0; i < SIZE; i++, j++)
		{
			Buffer[j] = Disk[b][i];
			count++;
			if (count == readc)
			{
				j++;
				break;
			}
		}
		Buffer[j] = '\0';
		cout << Buffer;
		ii = 0;
		b = FAT[b];		//准备读下一个盘块
	}
	cout << endl;
	uof[i_uof].readp = pos + readc;	//调整读指针
	return 1;
}

CloseComd

解释

根据命令、参数关闭文件,并从uof表中移除

注意点如下:

  • close命令只能有1个参数

操作流程如下:

  • 判断参数个数,若参数不为1个,则提示命令中缺少文件名并返回-1
  • 通过FindPath函数,在全局变量temppath中生成全路径名
  • 通过Check_UOF函数,在uof表中找到相应表项的索引;若未找到,则提示文件XXX未打开或不存在,不能关闭并返回-2
  • 更新文件对应的目录项指针的相关属性,通过置uof[i_uof].state = 0删除该文件在uof表中的信息,提示关闭文件XXX成功并返回1

命令

命令格式命令说明
close <文件名>关闭指定文件

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2文件未打开
1文件关闭成功

定义

int CloseComd(int k)				//close命令的处理函数:关闭文件 
{
	// close <文件名>,若指定文件已打开,则关闭之,即从UOF中删除该文件
	// 对应的表项。若文件未打开或文件不存在,分别给出有关信息。

	int i_uof;
	char attrib = '\0';
	FCB *p;
	if (k < 1)
	{
		cout << "\n命令中缺少文件名。\n";
		return -1;
	}
	FindPath(comd[1], attrib, 0, p);	//构成全路径且去掉“..”存于temppath中
	i_uof = Check_UOF(temppath);		//查UOF
	if (i_uof == S)
	{
		cout << "\n文件" << temppath << "未打开或不存在,不能关闭。\n";
		return -2;
	}
	else
	{
		uof[i_uof].state = 0;			//在UOF中清除该文件登记栏
		p = uof[i_uof].fp;			//取该文件的目录项位置指针
		p->Addr = uof[i_uof].faddr;	//保存文件的首块号
		p->Fsize = uof[i_uof].fsize;	//保存文件的大小
		cout << "\n关闭文件" << temppath << "成功。\n";
	}
	return 1;
}

DelComd

解释

删除指定的文件,即清除其目录项和回收其所占用磁盘空间

操作流程如下:

  • 通过ProcessPath函数取文件的首块号
  • 通过FindFCB函数来判断该文件是否存在,若不存在,则提示要删除的文件不存在并返回-2
  • 根据temppathFileName构造全路径名到gFilePath
  • 通过Check_UOF函数判断该文件是否已经打开:
    • 若已打开,则提示文件XXX正在使用,不能删除!并返回-3
    • 若未打开,提示用户确认是否删除:
      • 若用户未输入y,就不删除,并返回0
      • 若用户输入y,则通过PutUdtab函数尝试将被删除文件信息保存在udtab表中;若磁盘空间不足,询问用户是否彻底删除:
        • 若用户输入n,就不删除,并返回0
        • 若用户未输入n,就删除目录项
      • 将目录项的名称的首字符设为0xe5,代表这是一个被删除的目录项
      • 从盘块s开始回收其所往后链接的盘块链(回收磁盘空间),返回1

命令

命令格式命令说明
del <文件名>删除指定文件

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-2文件不存在
-3文件正在使用
0未删除文件
1文件删除成功

定义

int DelComd(int k)			//del(删除文件)命令处理函数
{
	// 删除文件:del <文件名>,删除指定的文件,即清除其目录项和回收
	// 其所占用磁盘空间。对于只读文件,删除前应询问用户,得到同意后
	// 方能删除。当指定文件正在使用时,显示"文件正在使用,不能删除"
	// 的信息,当指定文件不存在时给出错误信息。
	// 删除文件时,将该文件的有关信息记录到删除文件恢复信息表udtab中,
	// 以备将来恢复时使用。

	short i, s0, s;
	char yn, attr;
	char attrib = '\0', *FileName;
	char gFileName[PATH_LEN];	//存放文件全路径名
	FCB* fcbp;

	s0 = ProcessPath(comd[1], FileName, k, 1, '\20');//取FileName所在目录的首块号
	if (s0 < 1)			//路径错误
		return s0;		//失败,返回
	s = FindFCB(FileName, s0, attrib, fcbp);		//取FileName的首块号(查其存在性)
	if (s < 0)
	{
		cout << "\n要删除的文件不存在。\n";
		return -2;
	}
	strcpy(gFileName, temppath);
	i = strlen(temppath);
	if (temppath[i - 1] != '/')
		strcat(gFileName, "/");
	strcat(gFileName, FileName);	//构造文件的全路径名
	i = Check_UOF(gFileName);		//查UOF
	if (i < S)					//该文件已在UOF中
	{
		cout << "\n文件" << gFileName << "正在使用,不能删除!\n";
		return -3;
	}
	attr = fcbp->Fattrib & '\01';
	if (attr == '\01')
	{
		cout << "\n文件" << gFileName << "是只读文件,你确定要删除它吗?(y/n) ";
		cin >> yn;
		if (yn != 'Y' && yn != 'y')
			return 0;		//不删除,返回
	}
	i = PutUdtab(fcbp);		//被删除文件的有关信息保存到udtab表中
	if (i < 0)				//因磁盘空间不足,不能保存被删除文件的信息
	{
		cout << "\n你是否仍要删除文件 " << gFileName << " ? (y/n) : ";
		cin >> yn;
		if (yn == 'N' || yn == 'n')
			return 0;				//不删除返回
	}
	fcbp->FileName[0] = (char)0xe5;	//删除目录项
	while (s > 0)						//回收磁盘空间
	{
		s0 = s;
		s = FAT[s];
		FAT[s0] = 0;
		FAT[0]++;
	}
	return 1;
}

DirComd

解释

显示指定目录的内容

操作流程如下:

  • 根据命令解析出目录路径temppath和路径首块号s,这需要根据参数个数进行不同操作:
    • 参数个数为0——使用当前路径的curpath.cpathcurpath.fblock
    • 参数个数为1——通过FindPath函数查找该目录的首块号s
    • 参数个数为2——通过FindPath函数查找该目录的首块号s,通过GetAttrib函数获取第2个命令参数attrib
    • 若查找路径出错,则提示输入的路径错误!并返回-2
    • 若属性解析出错,返回-1
  • 从指定目录的盘块开始指向的盘块链中查找,根据属性参数进行筛选,打印目录中各个目录项的信息,返回1

命令

命令格式命令说明
dir [<目录名> [<属性>]]显示指定目录的内容,如dir /usr |h

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1属性参数有误
-2路径有误
-3文件正在使用
0未删除文件
1文件删除成功

定义

int DirComd(int k)	//dir命令,显示指定目录的内容(文件名或目录名等)
{
	// 命令形式:dir[ <目录名>[ <属性>]]
	// 命令功能:显示"目录名"指定的目录中文件名和第一级子目录名。若指
	// 定目录不存在,则给出错误信息。如果命令中没有指定目录名,则显示
	// 当前目录下的相应内容。若命令中无"属性"参数,则显示指定目录中"非
	// 隐藏"属性的全部文件名和第一级子目录名;若命令中有"属性"参数,则
	// 仅显示指定属性的文件名和目录名。h、r或s或两者都有,则显示隐藏属
	// 性或只读属性或既是隐藏又是只读属性的文件。属性参数的形式是"|<属
	// 性符号>",其中属性符号有r、h和s三种(不区分大小写),分别表示"只
	// 读"、"隐藏"和"系统"三种属性,它们可以组合使用且次序不限。例如"|rh"
	// 和"|hr"都表示要求显示同时具有"只读"和"隐藏"属性的文件和目录名。显
	// 示文件名时,显示该文件长度;显示目录名时,同时显示"<DIR>"的字样。

	// 举例:
	//		dir /usr |h	 
	// 上述命令显示根目录下usr子目录中全部"隐藏"属性的文件名和子目录名
	//		dir ..		 
	// 上述命令显示当前目录的父目录中全部"非隐藏"属性的文件和子目录名(包
	// 括"只读"属性的也显示,但一般不显示"系统"属性的,因为"系统"属性的对
	// 象一般也是"隐藏"属性的)。
	//
	// 学生可考虑将此函数修改成命令中的路径的最后允许是文件名的情况。
	// 另外还可以考虑含通配符的问题。

	short i, s;
	short filecount, dircount, fsizecount;	//文件数、目录数、文件长度累计
	char ch, attrib = '\0', attr, cc;
	FCB *fcbp, *p;

	filecount = dircount = fsizecount = 0;
	if (k < 1)	//命令无参数,显示当前目录
	{
		strcpy(temppath, curpath.cpath);
		s = curpath.fblock;	//当前目录的首块号保存于s
	}
	else if (k == 1)		//命令有1个参数(k=1)
	{
		if (comd[1][0] == '|')
		{
			i = GetAttrib(comd[1], attrib);
			if (i < 0) return i;
			strcpy(temppath, curpath.cpath);
			s = curpath.fblock;	//当前目录的首块号保存于s
		}
		else
		{
			s = FindPath(comd[1], '\020', 1, fcbp);	//找指定目录(的首块号)
			if (s < 1)
			{
				cout << "\n输入的路径错误!" << endl;
				return -2;
			}
		}
	}
	else		//命令有2个参数(k=2)
	{
		s = FindPath(comd[1], '\020', 1, fcbp);	//找指定目录(的首块号)
		if (s < 1)
		{
			cout << "\n输入的路径错误!" << endl;
			return -2;
		}
		i = GetAttrib(comd[2], attrib);
		if (i < 0) return i;
	}
	cout << "\nThe Directory of C:" << temppath << endl << endl;
	while (s > 0)
	{
		p = (FCB*)Disk[s];	//p指向该目录的第一个盘块
		for (i = 0; i < 4; i++, p++)
		{
			ch = p->FileName[0];	//取文件(目录)名的第一个字符
			if (ch == (char)0xe5)		//空目录项
				continue;
			if (ch == '\0')		//已至目录尾部
				break;
			attr = p->Fattrib&'\07';	//不考虑文件还是目录,只考虑属性
			if (attrib == 0)			//命令中没有指定属性
			{
				if (attr&'\02')		//不显示“隐藏”属性文件
					continue;
			}
			else
			{
				cc = attr & attrib;
				if (attrib != cc)		//只显示指定属性的文件
					continue;
			}
			cout << setiosflags(ios::left) << setw(20) << p->FileName;
			if (p->Fattrib >= '\20')	//是子目录
			{
				cout << "<DIR>\n";
				dircount++;
			}
			else
			{
				cout << resetiosflags(ios::left);
				cout << setiosflags(ios::right) << setw(10) << p->Fsize << endl;
				filecount++;
				fsizecount += p->Fsize;
			}
		}
		if (ch == '\0') break;
		s = FAT[s];		//指向该目录的下一个盘块
	}
	cout << resetiosflags(ios::left) << endl;
	cout << setiosflags(ios::right) << setw(6) << filecount << " file(s)";
	cout << setw(8) << fsizecount << " bytes" << endl;
	cout << setw(6) << dircount << " dir(s) " << setw(8) << SIZE * FAT[0];
	cout << " free" << endl;
	return 1;
}

CdComd

解释

改变当前路径

操作流程如下:

  • 判断命令参数个数
    • 个数为0,则显示当前路径,返回1
    • 个数超过1,则提示命令错误:参数太多并返回-1
    • 个数为1,则开始解析路径字符串
      • 若格式有误或通过FindPath函数未找到,都将提示路径名错误!并返回-2
      • 若当前路径curpath的相关属性,返回1

命令

命令格式命令说明
cd [<路径名>]改变当前路径

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2路径有误
1目录切换成功

定义

int CdComd(int k)
{
	// 当前目录(工作目录)转移到指定目录下。指定目录不存在时,给出错误信息。
	// 若命令中无目录名,则显示当前目录路径。

	short i, s;
	char attrib = (char)16;
	FCB* fcbp;
	if (k > 1)	//命令中多于1个参数,错误
	{
		cout << "\n命令错误:参数太多。\n";
		return -1;
	}
	if (k < 1)	//命令无参数,显示当前目录
	{
		cout << "\nThe Current Directory is C:" << curpath.cpath << endl;
		return 1;
	}
	else		//命令有一个参数,将指定目录作为当前目录
	{
		i = strlen(comd[1]);
		if (i > 1 && comd[1][i - 1] == '/')	//路径以"/"结尾,错误
		{
			cout << "\n路径名错误!\n";
			return -1;
		}
		s = FindPath(comd[1], attrib, 1, fcbp);	//找指定目录(的首块号)
		if (s < 1)
		{
			cout << "\n路径名错误!" << endl;
			return -1;
		}
		curpath.fblock = s;
		strcpy(curpath.cpath, temppath);
		if (!dspath)
			cout << "\n当前目录变为 C:" << curpath.cpath << endl;
		return 1;
	}
}

MdComd

解释

在指定路径下创建目录

操作流程如下:

  • 判断命令参数个数
    • 个数小于1或超过2,则提示错误:命令中没有目录名命令错误:参数太多并返回-1
    • 个数为12,则开始解析路径字符串,得到盘块号s
      • 通过IsName函数检查目录名是否合法,若不合法,则提示命令中的新目录名错误并返回-2
      • 通过FindFCB函数查找盘块s下有无已存在的同名目录,若存在,则提示错误:目录重名!并返回-2
      • 若命令参数个数为2,通过GetAttrib函数获取第2个命令参数attrib
      • 通过FindBlankFCB函数寻找空白目录项,若磁盘无空闲位置则返回-1
      • 通过M_NewDir函数创建新目录
        • 若磁盘空间已满而创建失败,则提示磁盘空间已满,创建目录失败并返回-1
        • 若创建成功,则返回-1

命令

命令格式命令说明
md <目录名>在指定路径下创建目录
md <目录名> [<属性>]在指定路径下创建指定属性的目录

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误 / 属性参数有误 / 磁盘空间已满
-2目录名不合法 / 重复
1目录创建成功

定义

int MdComd(int k)		//md命令处理函数
{
	// 命令形式:md <目录名>
	// 功能:在指定路径下创建指定目录,若没有指定路径,则在当前目录下创建指定目录。
	// 对于重名目录给出错误信息。目录与文件也不能重名。

	// 学生可以考虑命令中加“属性”参数,用于创建指定属性的子目录。命令形式如下:
	//		md <目录名>[ <属性>]
	// 属性包括R、H、S以及它们的组合(不区分大小写,顺序也不限)。例如:
	//		md user rh
	// 其功能是在当前目录中创建具有“只读”和“隐藏”属性的子目录user。

	short i, s, s0, kk;
	char attrib = (char)16, *DirName;
	FCB *p;

	kk = SIZE / sizeof(FCB);

	if (k < 1)
	{
		cout << "\n错误:命令中没有目录名。\n";
		return -1;
	}
	if (k > 2)
	{
		cout << "\n错误:命令参数太多。\n";
		return -1;
	}
	s = ProcessPath(comd[1], DirName, k, 0, attrib);
	if (s < 0)
		return s;		//失败,返回
	if (!IsName(DirName))		//若名字不符合规则
	{
		cout << "\n命令中的新目录名错误。\n";
		return -2;
	}
	i = FindFCB(DirName, s, attrib, p);
	if (i > 0)
	{
		cout << "\n错误:目录重名!\n";
		return -2;
	}
	if (k == 2)		//命令形式:md <目录名> |<属性符>
	{
		i = GetAttrib(comd[2], attrib);
		if (i < 0)
			return i;
	}
	s0 = FindBlankFCB(s, p);//找空白目录项
	if (s0 < 0)			//磁盘满
		return s0;
	s0 = M_NewDir(DirName, p, s, attrib);	//在p所指位置创建一新子目录项
	if (s0 < 0)		//创建失败
	{
		cout << "\n磁盘空间已满,创建目录失败。\n";
		return -1;
	}
	return 1;		//新目录创建成功,返回
}

RdComd

解释

删除指定路径的目录

操作流程如下:

  • 通过ProcessPath函数查找指定目录的父目录的首块号
    • 若参数个数有误,则返回-1
    • 若路径有误,则返回-3
  • 通过FindFCB函数去查找指定目录的首块号
    • 若未查找到,则提示要删除的目录不存在并返回-2
    • 若所找到的盘块号指向当前目录盘块号,则提示不能删除当前目录并返回0
  • 统计该目录的非空目录项数量,若目录非空,则提示目录XXX非空,不能删除并返回-4
  • 归还磁盘空间,删除目录,返回1

命令

命令格式命令说明
rd [<路径名>]删除指定路径的目录

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2要删除的目录不存在
-3目录路径有误
-4不能删除非空目录
0不能删除当前目录
1目录删除成功

定义

int RdComd(int k)
{
	// 若指定目录为空,则删除之,否则,给出"非空目录不能删除"的提示。
	// 不能删除当前目录。

	short i, j, count = 0, fs, s0, s;
	char attrib = (char)16, *DirName;
	FCB *p, *fcbp;
	fs = ProcessPath(comd[1], DirName, k, 1, attrib);	//返回DirName的父目录的首块号
	if (fs < 0)
		return fs;				//失败,返回
	s0 = s = FindFCB(DirName, fs, attrib, fcbp);//取DirName的首块号
	if (s < 1)
	{
		cout << "\n要删除的目录不存在。\n";
		return -2;
	}
	if (s == curpath.fblock)
	{
		cout << "\n不能删除当前目录。\n";
		return 0;
	}
	while (s > 0)		//循环查找,直到目录尾部
	{
		p = (FCB*)Disk[s];
		for (i = 0; i < 4; i++, p++)
		{
			if (p->FileName[0] != (char)0xe5 && p->FileName[0] != '\0')//累计非空目录项
				count++;
		}
		//s0=s;			//记下上一个盘块号
		s = FAT[s];		//取下一个盘块号
	}
	if (count > 1)
	{
		cout << "\n目录" << DirName << "非空,不能删除。\n";
		return -4;
	}
	//s0=fcbp->Addr;		//取DirName的首块号
	while (s0 > 0)			//归还目录DirName所占的磁盘空间
	{
		s = FAT[s0];			//记下第s0块的后续块号		
		FAT[s0] = 0;			//回收第s0块
		FAT[0]++;			//空闲盘块数增1
		s0 = s;				//后续块号赋予s0
	}
	fcbp->FileName[0] = (char)0xe5;	//删除DirName的目录项
	if (strcmp(temppath, "/") == 0)	//所删除的子目录在根目录
		return 1;
	//所删除的子目录DirName不在根目录时,对其父目录作以下处理
	s0 = s = fs;				//取DirName父目录的首块号
	while (s > 0)				//整理DirName的父目录空间(回收无目录项的盘块)
	{
		p = (FCB*)Disk[s];
		for (j = i = 0; i < 4; i++, p++)
			if (p->FileName[0] != (char)0xe5 && p->FileName[0] != '\0')//累计非空目录项
				j++;
		if (j == 0)
		{
			FAT[s0] = FAT[s];		//调整指针
			FAT[s] = 0;			//回收s号盘块
			FAT[0]++;			//空闲盘块数增1
			s = FAT[s0];
		}
		else
		{
			s0 = s;				//记下上一个盘块号
			s = FAT[s];			//s指向下一个盘块
		}
	}
	return 1;
}

RenComd

解释

重命名指定文件

操作流程如下:

  • 通过ProcessPath函数查找指定目录的父目录的首块号
    • 若参数个数有误,则返回-1
    • 若路径有误,则返回-3
  • 通过FindFCB函数去查找指定目录的首块号
    • 若未查找到,则提示要改名的文件不存在并返回-2
  • 根据temppathFileName构造全路径名到gFilePath
  • 通过Check_UOF函数判断该文件是否已经打开:
    • 若已打开,则提示文件XXX已经打开,不能改名!并返回-6
  • 通过IsName函数判断性文件名是否合法,若不合法,则提示命令中提供的新文件名错误并返回-4
  • 通过FindFCB函数查找盘块s下是否存在同名文件,存在则提示存在与新文件名同名的文件并返回-5
  • 若还有目标文件参数

命令

命令格式命令说明
ren <原文件名> <新文件名>重命名指定文件

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2要改名的文件不存在
-3路径有误
-4新文件名不合法
-5存在同名文件
-6文件已经打开,不能改名
1改名成功

定义

int RenComd(int k)	//ren命令的处理函数:文件改名
{
	// 命令形式:ren <原文件名> <新文件名>
	// 若原文件不存在,给出错误信息。
	// 若原文件存在,但正在使用,也不能改名,同样显示出错信息。
	// 应检查新文件名是否符合命名规则。

	short i, s0, s;
	char attrib = '\0', *FileName;
	char gFileName[PATH_LEN];	//存放文件全路径名
	FCB *fp, *fcbp;
	s0 = ProcessPath(comd[1], FileName, k, 2, '\20');//取FileName所在目录的首块号
	if (s0 < 1)			//路径错误
		return s0;		//失败,返回
	s = FindFCB(FileName, s0, attrib, fcbp);		//取FileName的首块号(查其存在性)
	if (s < 0)
	{
		cout << "\n要改名的文件不存在。\n";
		return -2;
	}
	strcpy(gFileName, temppath);
	i = strlen(temppath);
	if (temppath[i - 1] != '/')
		strcat(gFileName, "/");
	strcat(gFileName, FileName);	//构造文件的全路径名
	i = Check_UOF(gFileName);		//查UOF
	if (i < S)					//该文件已在UOF中
	{
		cout << "\n文件" << gFileName << "已经打开,不能改名!\n";
		return -6;
	}
	if (IsName(comd[2]))
	{
		fp = fcbp;						//保存指向要改名文件目录项的指针
		s = FindFCB(comd[2], s0, attrib, fcbp);	//查新文件名是否重名
		if (s < 0)			//不重名
		{
			strcpy(fp->FileName, comd[2]);
			return 1;		//正确返回
		}
		cout << "\n存在与新文件名同名的文件。\n";
		return -5;
	}
	cout << "\n命令中提供的新文件名错误。\n";
	return -4;
}

CopyComd

解释

复制指定文件

操作流程如下:

  • 分析参数个数,给出相应提示并返回-1
  • 通过ProcessPath函数查找指定目录的父目录的首块号
    • 若参数个数有误,则返回-1
    • 若路径有误,则返回-3
  • 通过FindFCB函数去查找指定目录的首块号
    • 若未查找到,则提示要复制的文件不存在并返回-2
  • 根据temppathFileName构造全路径名到gFilePath
  • 通过Check_UOF函数判断该文件是否已经打开:
    • 若已打开,则提示文件XXX已经打开,不能改名!并返回-6
  • 若命令参数个数为1,则同名复制到当前目录
  • 若命令参数个数为2,则同样要按上述步骤解析目标文件的相关属性
    • 若通过IsName函数判断文件名不合法,则提示命令中的目标文件名错误并返回-4
    • 若通过FindFCB函数去查找目标目录项的首块号,发现其与待复制文件名相同,则提示存在文件与目标文件同名不能同目录同名复制并返回-5
  • 通过FindBlankFCB函数尝试在盘块s所链接的盘块链(已满时尝试扩充盘块链)中找到一个空目录项,若找不到则提示复制文件失败并返回-7
  • 计算原文件所占盘块数,判断磁盘空间是否足够,若不够,则提示磁盘空间已满,不能复制文件并返回-8
  • 复制原文件信息,盘块不够时,通过getblock函数申请新盘块,复制完后返回1

命令

命令格式命令说明
copy <源文件名> [<目标文件名>]复制指定文件

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2要复制的文件不存在
-3路径有误
-4文件名不合法
-5存在同名文件
-6文件已经打开,不能复制
-7无法找到 / 创建空目录项
-8磁盘空间已满
1复制成功

定义

int CopyComd(int k)		//copy命令的处理函数:复制文件 
{
	// 复制文件:copy <源文件名> [<目标文件名>]
	// 命令功能:为目标文件建立目录项,分配新的盘块,并将源文件的内容复制到目标文件中
	// 和其他命令一样,这里的“文件名”,是指最后一个名字是文件的路径名。
	// 若目标文件与源文件所在的目录相同,则只能进行更名复制,此时目标文件名不能省;
	// 若目标文件与源文件所在的目录不同,则既可更名复制也可同名复制,同名复制时目标文件名可省。
	// 例如,命令
	//		copy mail email
	// (1) 若当前目录中不存在email(目录或文件),则该命令将当前目录中的文件mail,复制成
	//     当前目录下的文件email;
	// (2) 若当前目录下存在email,但email是子目录名,则将当前目录中的文件mail,复制到当
	//     前目录中的email子目录内,文件名与源文件相同(同名复制);此时若email目录内已经
	//     存在文件或目录mail,则出现重名错误;
	// (3) 若当前目录内存在email文件,则出现重名错误;
	// (4) 若当前目录内不存在源文件mail(或者虽然有mail,但它是子目录名),则也报错。
	//【特例】命令中无目标文件时,将源文件同名复制到当前目录中。例如,当前目录为/usr
	//		copy /box
	// 则上述命令把根目录中的文件box复制到当前目录/usr中,文件名仍为box。

	//【注】在同一目录中,各目录项不能重名(不管是文件名还是子目录名)。

	// 学生还可考虑使用通配符的多文件同名复制的情况(目标文件与源文件所在目录必须不同)。

	short int i, size, s01, s02, s1, s2, s22, b, b0, bnum;
	char attrib = '\0', *FileName1, *FileName2;
	char gFileName[PATH_LEN];	//存放文件全路径名
	FCB *fcbp, *fcbp1, *fcbp2;
	if (k < 1 || k>2)
	{
		cout << "\n命令中参数太多或太少。\n";
		return -1;
	}
	s01 = ProcessPath(comd[1], FileName1, k, 0, '\20');//取FileName所在目录的首块号
	if (s01 < 1)			//路径错误
		return s01;		//失败,返回
	s1 = FindFCB(FileName1, s01, attrib, fcbp);	//取FileName(源文件)的首块号(查其存在性)
	if (s1 < 0)
	{
		cout << "\n要复制的文件不存在。\n";
		return -2;
	}
	fcbp1 = fcbp;			//记下源文件目录项指针值
	strcpy(gFileName, temppath);
	i = strlen(temppath);
	if (temppath[i - 1] != '/')
		strcat(gFileName, "/");
	strcat(gFileName, FileName1);	//构造文件的全路径名
	i = Check_UOF(gFileName);			//查UOF
	if (i < S)						//该文件已在UOF中
	{
		cout << "\n文件" << gFileName << "已经打开,不能复制!\n";
		return -6;
	}
	if (k == 1)		//命令中无目标文件,同名复制到当前目录
	{
		s02 = curpath.fblock;	//取当前目录的首块号
		FileName2 = FileName1;
	}
	else	//k=2(命令中提供目标文件)的情况
	{
		s02 = ProcessPath(comd[2], FileName2, k, 0, '\20');//取FileName2所在目录的首块号
		if (s02 < 1)			//目标路径错误
			return s02;
	}
	if (!IsName(FileName2))		//若名字不符合规则
	{
		cout << "\n命令中的目标文件名错误。\n";
		return -4;
	}
	s2 = FindFCB(FileName2, s02, '\040', fcbp);	//取FileName2(目标文件)的首块号(查其存在性)
	if (s2 >= 0 && fcbp->Fattrib <= '\07')	//存在同名目标文件
	{
		cout << "\n存在文件与目标文件同名。\n";
		return -5;
	}
	if (s2 < 0)		//FileName2尚不存在,在s02为首块号的目录内复制目标文件
		s22 = s02;
	else			//FileName2存在,但它是目录名
	{
		s22 = s2;
		if (s2 != s01)		//源文件与目标文件不同目录
		{
			b = FindFCB(FileName1, s2, attrib, fcbp);//需查FileName2目录中有没有文件FileName1
			if (b >= 0)
			{
				cout << "\n有同名文件,不能复制。\n";
				return -4;
			}
			FileName2 = FileName1;	//缺省目标文件名,同名复制
		}
		else
		{
			cout << "\n不能同目录同名复制。\n";
			return -5;
		}
	}
	i = FindBlankFCB(s22, fcbp2);
	if (i < 0)
	{
		cout << "\n复制文件失败。\n";
		return -7;
	}
	size = fcbp1->Fsize;		//源文件的长度
	bnum = size / SIZE + (short)(size%SIZE > 0);	//计算源文件所占盘块数
	if (FAT[0] < bnum)
	{
		cout << "\n磁盘空间已满,不能复制文件。\n";
		return -8;
	}
	*fcbp2 = *fcbp1;						//源文件的目录项复制给目标文件
	strcpy(fcbp2->FileName, FileName2);	//写目标文件名
	b0 = 0;
	while (s1 > 0)		//开始复制文件内容
	{
		b = getblock();
		if (b0 == 0)
			fcbp2->Addr = b;		//目标文件的首块号
		else
			FAT[b0] = b;
		memcpy(Disk[b], Disk[s1], SIZE);	//复制盘块
		s1 = FAT[s1];				//准备复制下一个盘块
		b0 = b;
	}
	return 1;					//文件复制成功,返回
}

TypeComd

解释

显示指定文件的内容

操作流程如下:

  • 分析参数个数,给出相应提示并返回-1
  • 通过ProcessPath函数查找指定目录的父目录的首块号
    • 若参数个数有误,则返回-1
    • 若路径有误,则返回-3
  • 通过FindFCB函数去查找指定目录的首块号
    • 若未查找到,则提示文件XXX不存在并返回-3
  • 根据temppathFileName构造全路径名到gFilePath
  • Buffer读取文件内容并输出(若文件为空,则给出相应提示),返回1

命令

命令格式命令说明
type <文件名>显示指定文件的内容

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-3路径有误 / 文件不存在
1显示成功

定义

int TypeComd(int k)		//type命令处理函数(显示文件内容)
{
	// 显示文件内容:type <文件名>,显示指定文件的内容。
	// 若指定文件不存在,则给出错误信息。

	short i, s, size, jj = 0;
	char attrib = '\0', *FileName;
	char *Buffer;
	char gFileName[PATH_LEN];	//存放文件全路径名
	FCB* fcbp;

	if (k < 1)
	{
		cout << "\n命令中无文件名。\n";
		return -1;
	}
	s = ProcessPath(comd[1], FileName, k, 0, '\020');//取FileName所在目录的首块号
	if (s < 1)			//路径错误
		return s;		//失败,返回
	s = FindFCB(FileName, s, attrib, fcbp);		//取FileName的首块号(查其存在性)
	strcpy(gFileName, temppath);
	i = strlen(temppath);
	if (temppath[i - 1] != '/')
		strcat(gFileName, "/");
	strcat(gFileName, FileName);	//构造文件的全路径名
	if (s < 0)
	{
		cout << "\n文件" << gFileName << "不存在。\n";
		return -3;
	}
	if (s == 0)
		cout << "\n文件" << gFileName << "是空文件\n";
	else
	{
		size = fcbp->Fsize;
		Buffer = new char[size + 1];		//分配动态内存空间
		while (s > 0)
		{
			for (i = 0; i < SIZE; i++, jj++)
			{
				if (jj == size)
					break;
				Buffer[jj] = Disk[s][i];
			}
			if (i < SIZE)
				break;
			s = FAT[s];
		}
		Buffer[jj] = '\0';
		cout << Buffer << endl;
		delete[] Buffer;		//释放分配的动态内存空间
	}
	return 1;
}

HelpComd

解释

输出帮助信息

命令

命令格式命令说明
help输出帮助信息

参数

参数原型说明
void

返回值

返回值说明
void

定义

void HelpComd()				//help命令,帮助信息(显示各命令格式)
{
	cout << "\n* * * * * * * * * 本系统主要的文件操作命令简述如下 * * * * * * * * * *\n\n";
	cout << "create <文件名>[ <文件属性>]      ——创建新文件,文件属性是r、h或s。\n";
	cout << "open <文件名>                           ——打开文件,操作类型可为r、h或(与)s。\n";
	cout << "write <文件名> [<位置/app>[ insert]]    ——在指定位置写文件(有插入功能)。\n";
	cout << "read <文件名> [<位置m> [<字节数n>]]     ——读文件,从第m字节处读n个字节。\n";
	cout << "close <文件名>             ——关闭文件。\n";
	cout << "del <文件名>                            ——撤消(删除)文件。\n";
	cout << "dir [<路径名>] [|<属性>]                ——显示当前目录。\n";
	cout << "cd [<路径名>]                           ——改变当前目录。\n";
	cout << "md <路径名> [<属性>]                    ——创建指定目录。\n";
	cout << "rd [<路径名>]                           ——删除指定目录。\n";
	cout << "ren <旧文件名> <新文件名>               ——文件更名。\n";
	cout << "attrib <文件名> [±<属性>]              ——修改文件属性(r、h、s)。\n";
	cout << "copy <源文件名> [<目标文件名>]          ——复制文件。\n";
	cout << "type <文件名>                           ——显示文件内容。\n";
	cout << "rewind <文件名>                         ——将读、写指针移到文件第一个字符处。\n";
	cout << "fseek <文件名> <位置>                   ——将读、写指针都移到指定位置。\n";
	cout << "block <文件名>                          ——显示文件占用的盘块号。\n";
	cout << "closeall                                ——关闭当前打开的所有文件。\n";
	cout << "uof                                     ——显示UOF(用户打开文件表)。\n";
	cout << "undel [<路径名>]                        ——恢复指定目录中被删除的文件。\n";
	cout << "exit                                    ——退出本程序。\n";
	cout << "prompt                                  ——提示符是否显示当前目录(切换)。\n";
	cout << "fat                                     ——显示FAT表中空闲盘块数(0的个数)。\n";
	cout << "check                                   ——核对后显示FAT表中空闲盘块数。\n";
}

AttribComd

解释

修改文件属性

操作流程如下:

  • 分析参数个数,给出相应提示并返回-1
  • 通过FindPath函数查找指定目录项的首块号
    • 若未找到,则提示文件或目录不存在并返回-2
  • 若参数个数为1,则通过其目录项指针来查看相关文件的属性,返回1
  • 若参数个数为2
    • 通过Check_UOF文件检查文件是否已打开,若文件已经打开,则提示文件XXX正打开着,不能修改属性并返回-3
    • 通过按位或运算来写入属性参数,若属性参数有误,则提示命令中的属性参数错误并返回-4
    • 修改完成后,返回1

命令

命令格式命令说明
attrib <文件名> [±<属性>]查看 / 修改文件属性

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2文件或目录不存在
-3文件已经打开,不能改名
-4属性参数有误
1查看 / 修改文件属性成功

定义

int AttribComd(int k)	//attrib命令的处理函数:修改文件或目录属性 
{
	// 显示修改文件属性:attrib <文件名> [±<属性>]。若命令中无"文件属性"参数,
	// 则显示指定文件的属性;若命令中有"文件属性"参数,则修改指定文件的属性。"文
	// 件属性"的形式有“+r或+h或+s”和“-r或-h或-s”两种形式,前者为设置指定文件
	// 为"只读"或"隐藏"或"系统"属性,后者为去掉指定文件的"只读"或"隐藏"或"系统"
	// 属性。各属性可组合使用且顺序不限。例如:
	//		attrib user/boy +r +h
	// 其功能是设置当前目录下user子目录中的文件boy为只读、隐藏文件。又如
	//		attrib /usr/user/box -h -r -s
	// 上述命令的功能是取消文件/usr/user/box的"隐藏"、"只读"、"系统"属性。
	// 当命令中指定的文件已打开或不存在,给出错误信息;
	// 当命令中提供的参数错误,也显示出错信息。

	short i, j, i_uof, s;
	char Attrib, attrib = '\40';
	char Attr[5], Attr1[4] = "RHS";
	char attr[6][3] = { "+r","+h","+s","-r","-h","-s" };
	char or_and[6] = { '\01','\02','\04','\036','\035','\033' };
	FCB* fcbp;

	if (k < 1)
	{
		cout << "\n命令中没有指定文件名。\n";
		return -1;
	}
	s = FindPath(comd[1], attrib, 1, fcbp);	//寻找指定的文件或目录并返回其首块号
	if (s < 0)
	{
		cout << '\n' << temppath << "文件或目录不存在。\n";
		return -2;
	}
	if (k == 1)		//显示文件/目录的属性
	{
		Attrib = fcbp->Fattrib & '\07';
		if (Attrib == '\0')
			strcpy(Attr, "普通");
		else
		{
			for (i = 0; i < 3; i++)
			{
				if (Attrib & or_and[i])
					Attr[i] = Attr1[i];
				else
					Attr[i] = ' ';
			}
			Attr[i] = '\0';
		}
		cout << "\n" << temppath << "的属性是:" << Attr << endl;
		return 1;
	}
	if (fcbp->Fattrib <= '\07')		//若是文件,要查其是否已被打开
	{
		i_uof = Check_UOF(temppath);	//查UOF
		if (i_uof < S)
		{
			cout << "\n文件" << temppath << "正打开着,不能修改属性。\n";
			return -3;
		}
	}
	for (i = 2; i <= k; i++)		//处理属性参数
	{
		for (j = 0; j < 6; j++)
			if (_stricmp(comd[i], attr[j]) == 0)
			{
				if (j < 3)
					fcbp->Fattrib = fcbp->Fattrib | or_and[j];
				else
					fcbp->Fattrib = fcbp->Fattrib & or_and[j];
				break;
			}
		if (j == 6)
		{
			cout << "\n命令中的属性参数错误。\n";
			return -4;
		}

	}
	return 1;	//修改属性完成,返回
}

UofComd

解释

显示当前用户“打开文件表”

操作流程如下:

  • 遍历uof表,打印相关属性

命令

命令格式命令说明
uof显示当前用户“打开文件表”

参数

参数原型说明
void

返回值

返回值说明
void

定义

void UofComd()	//uof命令,显示当前用户“打开文件表”
{
	//显示用户已打开文件表UOF的内容

	int i, k;
	char ch;
	for (k = i = 0; i < S; i++)
		k += uof[i].state;
	if (k > 0)
	{
		cout << "\n打开文件表UOF的内容如下:\n\n"
			<< "文件名                       文件属性  "
			<< "首块号  文件长度  状态  读指针  写指针\n";
		for (i = 0; i < S; i++)
		{
			if (uof[i].state == 0)
				continue;					//空目录项
			cout.setf(ios::left);
			cout << setw(32) << uof[i].fname;	//显示文件名 
			ch = uof[i].attr;
			switch (ch)
			{
			case '\0': cout << "普通    ";
				break;
			case '\01': cout << "R       ";
				break;
			case '\02': cout << "H       ";
				break;
			case '\03': cout << "RH      ";
				break;
			case '\04': cout << "S       ";
				break;
			case '\05': cout << "RS      ";
				break;
			case '\06': cout << "HS      ";
				break;
			case '\07': cout << "RHS     ";
				break;
			default: cout << "错误    ";
			}
			cout << setw(8) << uof[i].faddr;	//首块号
			cout << setw(8) << uof[i].fsize;	//文件大小
			k = uof[i].state;
			if (k == 1)
				cout << " 建立   ";			//状态为“建立” 
			else
				cout << " 打开   ";			//状态为“打开” 
			cout << setw(8) << uof[i].readp;
			cout << uof[i].writep << endl;		//读指针 
		}
	}
	else
		cout << "目前尚无打开的文件。\n";
}

CloseallComd

解释

关闭当前用户的所有文件

操作流程如下:

  • 遍历uof表,将state属性不为0的表项对应的目录项的内容保存,并将表项的state属性置0代表从uof表中清空

命令

命令格式命令说明
closeall关闭当前用户的所有文件

参数

参数原型说明
int disp提示指示信号,决定是否需要打印提示信息

返回值

返回值说明
void

定义

void CloseallComd(int disp)    //closeall命令,关闭当前用户的所有文件
{
	int i_uof, j, k;
	FCB *p;
	for (k = i_uof = 0; i_uof < S; i_uof++)
	{
		j = uof[i_uof].state;	//UOF中状态>0为有效登记项
		if (j > 0)
		{
			k++;  //已打开文件计数
			uof[i_uof].state = 0;			//在UOF中清除该文件登记栏
			p = uof[i_uof].fp;			//取该文件的目录项位置指针
			p->Addr = uof[i_uof].faddr;	//保存文件的首块号
			p->Fsize = uof[i_uof].fsize;	//保存文件的大小
			cout << "\n文件" << uof[i_uof].fname << "已关闭.\n";
		}
	}
	if (!disp)
		return;
	if (k == 0)
		cout << "\n你没有打开文件,故无文件可关闭。\n\n";
	else
		cout << "\n共关闭 " << k << " 个文件.\n\n";
}

blockf

解释

显示文件或目录占用的盘块号

操作流程如下:

  • 分析参数个数,给出相应提示并返回-1
  • 通过FindPath函数查找指定目录项的首块号
    • 若未找到,则提示路径名错误!并返回-2
  • 循环遍历从盘块s开始链接的盘块链,输出链中每个盘块的块号,返回1

命令

命令格式命令说明
block <文件名>显示文件或目录占用的盘块号

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2路径错误
1查看 / 修改文件属性成功

定义

int blockf(int k)	//block命令处理函数(显示文件或目录占用的盘块号)
{
	short s;
	char attrib = '\040';		//32表示任意(文件或子目录)目录项都可以
	FCB* fcbp;

	if (k != 1)
	{
		cout << "\n命令中参数个数错误。\n";
		return -1;
	}
	s = FindPath(comd[1], attrib, 1, fcbp);	//找指定目录(的首块号)
	if (s < 1)
	{
		cout << "\n路径名错误!" << endl;
		return -2;
	}
	cout << "\n" << temppath << "占用的盘块号为:";
	while (s > 0)
	{
		cout << s << "  ";
		s = FAT[s];
	}
	cout << endl;
	return 1;
}

RewindComd

解释

读、写指针移到文件开头

操作流程如下:

  • 分析参数个数,给出相应提示并返回-1
  • 通过FindPath函数构造全路径名,将结果存入全局变量temppath
  • 通过Check_UOF文件检查文件是否已打开,若文件未打开,则提示文件XXX未打开或不存在,不能操作并返回-3
  • uof表中修改指定表项的内容,修改完成后,返回1

命令

命令格式命令说明
rewind <文件名>读、写指针移到文件开头

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2路径错误
-3文件未打开 / 文件不存在
1修改读、写指针成功

定义

int RewindComd(int k)	//rewind命令的处理函数:读、写指针移到文件开头 
{
	// 命令形式:rewind <文件名>
	// 对指定文件操作,同时该文件变为当前操作文件

	int i_uof;
	char attrib = '\0';
	FCB* fcbp;

	if (k != 1)
	{
		cout << "\n命令参数个数错误。本命令只能有1个参数。\n";
		return -1;
	}
	FindPath(comd[1], attrib, 0, fcbp);		//构成全路径且去掉“..”存于temppath中
	i_uof = Check_UOF(temppath);		//查UOF
	if (i_uof == S)
	{
		cout << "\n文件" << temppath << "未打开或不存在,不能操作。\n";
		return -3;					//操作失败返回
	}
	if (uof[i_uof].faddr > 0)			//若是空文件
		uof[i_uof].readp = 1;			//读指针设定为0
	else
		uof[i_uof].readp = 0;			//非空文件的读指针设定为1
	uof[i_uof].writep = 1;			//文件的写指针设定为1
	return 1;						// 修改成功,返回
}

FseekComd

解释

将读、写指针移到指定位置n

操作流程如下:

  • 分析参数个数,给出相应提示并返回-1
  • 通过FindPath函数构造全路径名,将结果存入全局变量temppath
  • 通过Check_UOF文件检查文件是否已打开,若文件未打开,则提示文件XXX未打开或不存在,不能操作并返回-2
  • uof表中找到指定表项
    • 若该文件为空文件(fsize == 0),则提示文件XXX是空文件,不能进行此操作并返回-3
    • 若位置参数n不合法,则提示位置参数错误。该参数必须在1和XX之间并返回-4
    • 修改表项的指针位置,返回1

命令

命令格式命令说明
fseek <文件名> <n>将读、写指针移到指定位置n

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2文件未打开 / 文件不存在
-3空文件无法操作
-4位置参数有误
1修改读、写指针成功

定义

int FseekComd(int k)	//fseek命令的处理函数 
{
	// 命令形式:fseek <文件名> <n>
	// 功能:将读、写指针移到指定位置n处

	int i_uof, n;
	char attrib = '\0';
	FCB* fcbp;

	if (k != 2)
	{
		cout << "\n命令参数个数错误。本命令必须有2个参数。\n";
		return -1;
	}
	n = atoi(comd[2]);
	FindPath(comd[1], attrib, 0, fcbp);		//构成全路径且去掉“..”存于temppath中
	i_uof = Check_UOF(temppath);		//查UOF
	if (i_uof == S)
	{
		cout << "\n文件" << temppath << "未打开或不存在,不能操作。\n";
		return -2;					//操作失败返回
	}
	if (uof[i_uof].fsize == 0)		//空文件
	{
		cout << "\n" << temppath << "是空文件,不能进行此操作。\n";
		return -3;
	}
	if (n <= 0 || n > uof[i_uof].fsize + 1)
	{
		cout << "\n位置参数错误。该参数必须在1和" << uof[i_uof].fsize + 1 << "之间。\n";
		return -4;
	}
	uof[i_uof].readp = n;				//读指针设定为n
	uof[i_uof].writep = n;			//写指针设定为n
	return 1;						//修改成功,返回
}

FatComd

解释

输出磁盘剩余空闲块数FAT[0]

命令

命令格式命令说明
fat输出磁盘剩余空闲块数

参数

参数原型说明
void

返回值

返回值说明
void

定义

void FatComd()	//若输入"fat"
{
	cout << "\n当前磁盘剩余空闲块数为" << FAT[0] << endl;
}

CheckComd

解释

核对后显示FAT表中空闲盘块数、盘块分配指针ffbp、删除文件恢复表指针Udelp

命令

命令格式命令说明
check核对后显示FAT表中空闲盘块数

参数

参数原型说明
void

返回值

返回值说明
void

定义

void CheckComd()		//check命令
{
	cout << "\n当前磁盘空闲是:" << FAT[0] << endl;
	int j = 0, i = 2;
	for (; i < K; i++)
		if (FAT[i] == 0)
			j++;
	FAT[0] = j;
	cout << "重新检查后,磁盘的空闲块是:" << FAT[0] << endl;
	cout << "\nffbp=" << ffbp << endl;
	cout << "Udelp=" << Udelp << endl;
}

ExitComd

解释

退出系统

操作流程如下:

  • 通过CloseallComd函数关闭并保存所有文件,防止数据丢失
  • 询问是否要将本次的FAT表、磁盘Disk、删除文件恢复表Udtab存入本地文件
    • 若用户输入y,通过save_FATsave_Disksave_UdTab将三张表的数据存入本地文件
  • 退出系统

命令

命令格式命令说明
exit退出系统

参数

参数原型说明
void

返回值

返回值说明
void

定义

void ExitComd()		//exit命令处理
{
	char yn;
	CloseallComd(0);		//关闭所有打开的文件以防数据丢失
	cout << "\n退出时FAT、Disk、Udtab是否要存盘?(y/n) ";
	cin >> yn;
	if (yn == 'Y' || yn == 'y')
	{
		save_FAT();			//FAT表存盘
		save_Disk();		//磁盘块中存储的内容
		save_UdTab();		//保存被删除文件信息表
	}
	//delete [] Disk;
	exit(0);
}

UndelComd

解释

恢复当前目录 / 指定目录中,被删除的文件

  • 分析参数个数,给出相应提示并返回-1
  • 根据参数个数更新temppath和首块号s的值:
    • 命令参数个数为0,获取当前目录的curpath.cpathcurpath.fblock
    • 命令参数个数为1,通过FindPath查找路径对应的首块号,若路径错误,则提示命令中所给的路径错误并返回-2
  • 到首块号为s的盘块链中查找被删除文件的目录项(FileName[0] == 0xe5
  • 通过Udfile函数对文件进行恢复,记录恢复的文件个数并输出,返回1

命令

命令格式命令说明
undel恢复当前目录中被删除的文件
undel <目录名>恢复指定目录中被删除的文件

参数

参数原型说明
int k命令参数个数

返回值

返回值说明
-1命令参数个数有误
-2路径错误
1修改读、写指针成功

定义

int UndelComd(int k)		//undel命令
{
	// 命令形式:undel [<目录名>]
	// 命令功能:恢复指定目录中被删除的文件
	// 具体有如下2种命令形式:
	//		undel——恢复当前目录中被删除的文件
	//		undel <目录名>——恢复指定目录中被删除的文件

	short i, s, s0, cc = 0;		//cc是恢复文件计数变量
	char *fn;
	FCB *fcbp1;
	if (k > 1)
	{
		cout << "\n命令不能有参数。\n";
		return -1;
	}
	if (k < 1)		//若命令中无参数
	{
		strcpy(temppath, curpath.cpath);
		s0 = s = curpath.fblock;
	}
	else
	{
		s0 = s = FindPath(comd[1], '\020', 1, fcbp1);
		if (s < 0)
		{
			cout << "\n命令中所给的路径错误。\n";
			return -2;
		}
	}
	while (s > 0)			//在首块号为s的目录找被删除文件的表项,直到目录尾部
	{
		fcbp1 = (FCB*)Disk[s];
		for (i = 0; i < 4; i++, fcbp1++)
		{
			if (fcbp1->FileName[0] == (char)0xe5)		//找到可能进行删除恢复的目录项
			{
				fn = &(fcbp1->FileName[1]);
				Udfile(fcbp1, s0, fn, cc);
			}
		}
		s = FAT[s];	//取下一个盘块号
	}
	cout << "\n共恢复了 " << cc << " 个被删除的文件。\n";
	return 1;
}

PromptComd

解释

提示符是否显示当前目录(切换时)

命令

命令格式命令说明
prompt提示符是否显示当前目录(切换时)

参数

参数原型说明
void

返回值

返回值说明
void

定义

void PromptComd()			//prompt命令
{
	dspath = !dspath;
}

UdTabComd

解释

显示删除文件恢复表udtab的内容

命令

命令格式命令说明
udtab显示删除文件恢复表udtab的内容

参数

参数原型说明
void

返回值

返回值说明
void

定义

void UdTabComd()			//udtab命令
{
	//显示删除文件恢复表udtab的内容

	cout << "\n恢复被删除文件信息表(UdTab)内容如下:\n\n";
	cout << "文件路径名                      " << "文件名        "
		<< "首块号      " << "存储块号" << endl;
	for (int i = 0; i < Udelp; i++)
		cout << setiosflags(ios::left) << setw(32) << udtab[i].gpath
		<< setw(15) << udtab[i].ufname << setw(12) << udtab[i].ufaddr
		<< setw(8) << udtab[i].fb << endl;
}

3、辅助函数

ProcessPath

解释

根据完整路径名查找到最后一级目录存入temppath,返回这个最后一级目录的首块号

操作流程如下:

  • 判断参数个数是否符合要求,如果不满足!n || k == n,提示参数个数错误并返回
  • 从路径字符串尾部向前寻找第一个字符/,将这个/字符以后的字符全部存入字符串引用变量参数Name
  • 判断i的值,更新temppath字符串,返回相应目录的首块号:
    • i == -1:说明是在当前目录下创建文件,令temppathcurpath.cpath返回当前目录首块号curpath.fblock
    • i == 0:说明是在根目录下创建文件,令temppath/返回根目录首块号
    • i >= 1:说明是需要根据某个绝对路径 / 相对路径去查找目录,调用FindPath函数查找对应目录项的首块号s,若s为负,提示路径有误并返回

参数

参数原型说明
char* path完整路径名(包括目录项名)字符串,形如user/bin/a.xml
char* &Name用来存放对解析出的目录项名的引用的变量
int k实际的命令参数个数
int n需要的命令参数个数 { n = 0 参 数 个 数    k    任 意 n > 0 参 数 个 数    k =    n \begin{cases}n=0 & 参数个数\;k\;任意\\ n>0 & 参数个数\;k=\;n \end{cases} {n=0n>0kk=n
char attrib文件属性

返回值

返回值说明
-1命令参数个数错误
-3路径名有误
其他返回参数path去掉目录项名字Name后、最后一级目录所指向的首块号

定义

int ProcessPath(char* path, char* &Name, int k, int n, char attrib)
{
	// 将path中最后一个名字分离出来,并由引用参数Name带回;
	// 返回path中除掉Name后,最后一个目录的地址(首块号);
	// 必要时调用函数FindPath(),并通过全局变量temppath返
	// 回path(去掉Name后)的全路径名(绝对路径名)

	short i, len, s;
	FCB* fcbp;

	if (n && k != n)	//n=0,参数个数k任意,n>0,必须k=n
	{
		cout << "\n命令参数个数错误!\n";
		return -1;
	}
	len = strlen(path);
	for (i = len - 1; i >= 0; i--)
		if (path[i] == '/')
			break;
	Name = &path[i + 1];		//取路径中最后一个名字
	if (i == -1)
	{
		s = curpath.fblock;
		strcpy(temppath, curpath.cpath);
	}
	else
	{
		if (i == 0)
		{
			s = 1;
			strcpy(temppath, "/");
		}
		else
		{
			path[i] = '\0';
			s = FindPath(path, attrib, 1, fcbp);
			if (s < 1)
			{
				cout << "\n路径名错误!\n";
				return -3;
			}
		}
	}
	return s;
}

FindPath

解释

操作流程如下:

  • 判断是否为根目录,是根目录的话设置temppath/后,可以直接返回根目录的盘块号1
  • 根据路径字符串pp的首字符判断是绝对路径还是相对路径,初始化拼接前的temppath和首块号s
  • 根据/对路径pp进行分解,将每次分解的目录项名称存入path[i]中(超长则截取前FILENAME_LEN-1个字符)
    在解析过程中,如果遇到某个子目录名为空,则返回-1
  • 根据paths[i]拼接出完整的temppath,解析其中的..(路径包含根目录的父目录时,返回-1
  • 根据ffcb决定,是否需要调用FindFCB函数,返回相应的目录项首块号,并用fcbp引用相应目录项指针

参数

参数原型说明
char* pp查找路径字符串,形如/usr/bin
char attrib文件属性
int ffcb决定是否调用FindFCB函数
FCB*& fcbp目录项指针的引用

返回值

返回值说明
-1路径错误
其他查找路径对应目录项的首块号

定义

int FindPath(char *pp, char attrib, int ffcb, FCB* &fcbp)
{
	// 查找命令中给定的路径,确定路径的正确性,并返回路径中最后一个
	// 名字(目录名)代表的目录的地址(首块号);对路径进行处理(去掉路
	// 径中的“..”),构成一个全路径名存于temppath中;当函数参数ffcb
	// 非零时,通过调用FindFCB( )函数,使本函数成功返回时,FCB类型的
	// 引用参数指针变量fcbp指向路径最后一个目录的目录项。

	short i, j, len, s = 0;
	char paths[60][FILENAME_LEN];	//分解路径用(路径中最多不超过60个名字)
	char *q, Name[PATH_LEN];

	strcpy(temppath, "/");
	if (strcmp(pp, "/") == 0)	//是根目录
		return 1;			//返回根目录的首块号
	if (*pp == '/')			//绝对路径,从根目录开始
	{
		s = 1;				//根目录的首块号
		pp++;
	}
	else
	{
		s = curpath.fblock;	//相对路径,从当前目录开始
		strcpy(temppath, curpath.cpath);
	}
	j = 0;
	while (*pp != '\0')	//对命令中的路径分解
	{
		for (i = 0; i < PATH_LEN; i++, pp++)
		{
			if (*pp != '/' && *pp != '\0')
				Name[i] = *pp;
			else
			{
				if (i > 0)
				{
					Name[i] = '\0';
					if (i > FILENAME_LEN - 1)	//名字过长则截取前FILENAME_LEN-1个字符
						Name[FILENAME_LEN - 1] = '\0';
					strcpy(paths[j], Name);
					j++;
				}
				else
					return -1;		//路径错误
				if (*pp == '/') pp++;
				break;			//已处理到字符串尾部
			}
		}
	}
	for (i = 0; i < j; i++)
	{
		if (strcmp(paths[i], "..") == 0)
		{
			if (strcmp(temppath, "/") == 0)
				return -1;		//路径错误(根目录无父目录)
			len = strlen(temppath);
			q = &temppath[len - 1];
			while (*q != '/') q--;
			*q = '\0';
			if (*temppath == '\0')
			{
				*temppath = '/';
				temppath[1] = '\0';
			}
		}
		else
		{
			if (strcmp(temppath, "/") != 0)
				strcat(temppath, "/");
			strcat(temppath, paths[i]);
		}
		if (ffcb)
		{
			s = FindFCB(paths[i], s, attrib, fcbp);
			if (s < 0)
				return s;
		}
	}
	return s;
}

FindFCB

解释

从盘块s开始,查找名为Name且符合属性attrib的目录项,找到后将相应目录项的指针传给引用对象fcbp

操作流程如下:

  • 从盘块Disk[s]开始,遍历查找路径目录下所有目录项(一个盘块4个目录项,找完就查找其下一个盘块,直到没有下一个盘块为止)
  • 找到名称符合的目录项后,判断文件属性是否符合,符合则返回相应目录项的首块号

参数

参数原型说明
char* Name查找路径字符串
int s查找起始块
char attrib文件属性
FCB* &fcbp目录项指针的引用

返回值

返回值说明
-1路径错误
-2访问完盘块下的所有目录项后仍未找到
-3找到了但文件属性不符
其他查找路径对应目录项的首块号

定义

int FindFCB(char *Name, int s, char attrib, FCB* &fcbp)
{
	// 从第s块开始,查找名字为Name且符合属性attrib的目录项
	// 给定名字Name没有找到返回负数,找到返回非负数(找目录时返回恒正)
	// 函数正确返回时,引用参数指针变量fcbp指向Name目录项。

	int i;
	char ch, Attrib;
	while (s > 0)
	{
		fcbp = (FCB*)Disk[s];
		for (i = 0; i < 4; i++, fcbp++)		//每个盘块4个目录项
		{
			ch = fcbp->FileName[0];
			if (ch == (char)0xe5)
				continue;
			if (ch == '\0')
				return -1;		//路径错误(至该目录尾部仍未找到)
			if (strcmp(Name, fcbp->FileName) == 0)	//名字找到
			{
				if (attrib == '\040')		//attrib为32时,文件、子目录不限
					return fcbp->Addr;
				Attrib = fcbp->Fattrib;
				if (attrib == '\020' && Attrib >= attrib)	//子目录属性
					return fcbp->Addr;
				if (attrib == '\0' && Attrib <= '\07')		//文件属性(找的是文件)
					return fcbp->Addr;
				return -3;			//名字符合但属性不对仍然没有找到
			}
		}
		s = FAT[s];		//取下一个盘块号
	}
	return -2;
}

IsName

解释

判断目录项名称是否符合以下规则:

  • 名称不能为空
  • 名称不能以字符.开头
  • 名称不能包含以下字符——" * + , / : ; < = > ? [ \ ] | space(空格)(检测到包含时,给出提示名字中不能包含字符XXX。

另外,名称的长度可以超过FILENAME_LEN - 1个字符,但是只有前FILENAME_LEN - 1个字符有效

参数

参数原型说明
char* Name查找路径字符串

返回值

返回值说明
-1路径错误
-2访问完盘块下的所有目录项后仍未找到
-3找到了但文件属性不符
其他查找路径对应目录项的首块号

定义

bool IsName(char* Name)
{
	// 判断名字是否符合如下规则:
	// (1) 名字长度不能超过FILENAME_LEN-1个字节,即10个字节。
	//     允许输入的名字超过10个字符,但只有前10个字符有效;
	// (2) 名字一般由字母(区分大小写)、数字、下划线等组成,名字允许是汉字;
	// (3) 名字不能包含以下16个字符之一:
	//		" * + , / : ; < = > ? [ \ ] | space(空格)
	// (4) 名字中允许包含字符“.”,但它不能是名字的第一个字符,故“.”、
	//    “.abc”、“..”和“..abc”等都是不合法的名字。

	int i, len, Len = FILENAME_LEN - 1;
	bool yn = true;
	char ch;
	len = strlen(Name);
	if (len == 0)
		return false;
	if (Name[0] == '.')		//名字第一个字符不能是字符'.'
		return false;
	if (len > Len)			//若名字过长,截去多余的尾部
	{
		Name[Len] = '\0';
		len = Len;
	}
	for (i = 0; i < len; i++)
	{
		ch = Name[i];
		if (isunname(ch))	//若名字中含有不合法符号
		{
			yn = false;
			break;
		}
	}
	if (!yn)
		cout << "\n名字中不能包含字符'" << ch << "'。\n";
	return yn;
}

FindBlankFCB

解释

寻找首块号为s的目录中的空目录项

操作流程如下:

  • FCB指针fcbp1从起始盘块号s开始遍历查找路径目录下所有目录项(一个盘块4个目录项,找完就查找其下一个盘块,直到没有下一个盘块为止);如果找到了相应的目录项,则设置当前fcbp1的相关属性为空目录项属性,返回1
  • 如果在上一步没有成功找到,s0会存储盘块起始盘块号s到的最后一个盘块,需要进行目录判断再进行操作:
    • 根目录:当根目录满后,不能再新建目录项,提示根目录已满,不能再创建目录项并返回-1
    • 非根目录:尝试通过getblock函数获取一块空闲盘块s;若无空闲盘块,提示磁盘空间已满,创建目录失败并返回-1
  • FAT[s0] = s构成FAT链,将空闲盘块s中的4个目录项名称设为空,将第1个目录项设为空目录项,返回1

参数

参数原型说明
short s查询起始首块号
FCB* &fcbp1目录项指针的引用

返回值

返回值说明
-1根目录已满 / 磁盘空间已满,不允许再创建新目录项
1成功创建新目录项

定义

int FindBlankFCB(short s, FCB* &fcbp1)	//寻找首块号为s的目录中的空目录项
{
	short i, s0;
	while (s > 0)			//在首块号为s的目录找空登记栏,直到目录尾部
	{
		fcbp1 = (FCB*)Disk[s];
		for (i = 0; i < 4; i++, fcbp1++)
			if (fcbp1->FileName[0] == (char)0xe5 || fcbp1->FileName[0] == '\0')
			{
				fcbp1->Addr = fcbp1->Fsize = 0;		//假设为空目录项
				return 1;						//找到空目录项,成功返回
			}
		s0 = s;		//记下上一个盘块号
		s = FAT[s];	//取下一个盘块号
	}
	if (strcmp(temppath, "/") == 0)	//若是根目录
	{
		cout << "\n根目录已满,不能再创建目录项。\n";
		return -1;
	}
	s = getblock();	//取一空闲盘快
	if (s < 0)		//无空闲盘快
	{
		cout << "\n磁盘空间已满,创建目录失败。\n";
		return -1;
	}
	FAT[s0] = s;		//构成FAT链
	fcbp1 = (FCB*)Disk[s];
	for (i = 0; i < 4; i++, fcbp1++)
		fcbp1->FileName[0] = '\0';	//置空目录标志
	fcbp1 = (FCB*)Disk[s];
	fcbp1->Addr = fcbp1->Fsize = 0;		//假设为空目录项
	return 1;
}

getblock

解释

获取一个空闲盘块

操作流程如下:

  • 根据FAT[0]判断空闲盘块数,若无空闲盘块,返回-1
  • FAT[ffbp]处开始查找空闲盘块(FAT[i] == 0表示盘块空闲);若到结尾FAT[K-1]都没有找到空闲盘块,则从开始处FAT[1]找到FAT[ffbp],这类似循环首次适应算法
  • 找到空闲盘块b后,置查找标记ffbp = b+1ffbp == K时,需要从头查找起,再置ffbp = 1),更新剩余空闲盘块数FAT[0]
  • 设置FAT[b]尾标记-1返回相应的空闲盘块号

参数

参数原型说明
void

返回值

返回值说明
-1无空闲盘块
其他正值获取到的空闲盘块号

定义

int getblock()	//获得一个空闲盘块,供fappend()函数调用
{
	short b;
	if (FAT[0] == 0)	//FAT[0]中是磁盘空闲块数
		return -1;	//磁盘已满(已无空闲盘块)
	for (b = ffbp; b < K; b++)
		if (!FAT[b])
			break;
	if (b == K)
	{
		for (b = 1; b < ffbp; b++)
			if (!FAT[b]) break;
	}
	ffbp = b + 1;
	if (ffbp == K) ffbp = 1;
	FAT[0]--;	//盘块数减1
	FAT[b] = -1;	//置盘块已分配标志(此处不妨假设其为文件尾)
	return b;	//返回取得的空闲盘块号
}

Put_UOF

解释

根据参数gFileNamestatusfcbp构造相关的UOF对象,存入uof表的第i项中

其中,UOF对象的读指针位置和读写指针位置需要根据fcbp->Fsize进行设置

参数

参数原型说明
char* gFileName文件的全路径名
int i放入文件打开表位置的索引号
short status文件状态
FCB* fcbp目录项指针

返回值

返回值说明
void

定义

void Put_UOF(char *gFileName, int i, short status, FCB* fcbp)
{
	strcpy(uof[i].fname, gFileName);	//复制文件全路径名
	uof[i].attr = fcbp->Fattrib;		//复制文件属性
	uof[i].faddr = fcbp->Addr;		//文件的首块号(0代表空文件)
	uof[i].fsize = fcbp->Fsize;
	uof[i].fp = fcbp;
	uof[i].state = status;					//打开状态
	if (fcbp->Fsize > 0)				//若文件非空
		uof[i].readp = 1;				//读指针指向文件开头
	else
		uof[i].readp = 0;				//读指针指向空位置
	uof[i].writep = fcbp->Fsize + 1;	//写指针指向文件末尾
}

Check_UOF

解释

检查uof表中有无指定文件

操作流程如下:

  • 遍历整个uof表,匹配全路径名与Name相同的表项,返回其索引i
  • 若找不到,则返回S

参数

参数原型说明
char* Name文件的全路径名

返回值

返回值说明
小于Si找到指定文件,位于uof[i]
S未找到指定文件

定义

int Check_UOF(char *Name)		//检查UOF中有无命令中指定的文件
{
	int i;
	for (i = 0; i < S; i++)			//查用户打开文件表UOF
	{
		if (uof[i].state == 0)	//空表项
			continue;
		if (strcmp(Name, uof[i].fname) == 0)	//找到
			break;
	}
	return i;
}

buffer_to_file

解释

将字符串Buffer写入文件

操作流程如下:

  • 判断Buffer的内容
    • 为空,则将文件设置为空文件,调用``buffer_to_file函数回收文件磁盘空间,**返回**1`
    • 非空,计算文件比原来需要多占用的盘块数,和FAT[0]比较
      • 空间不足,提示磁盘空间不足,不能将信息写入文件并返回0
      • 空间足够,根据首块号s判断其是否为空文件,若为空文件,调用getblock函数为其分配首个盘块,记入其FCB结构中
  • 将长度为len的字符串写入第s个盘块Disk[s]中(写入时\n需要单独处理);如果一个盘块用完了则需要继续调用getblock函数为其分配盘块号
  • 写入完成后,改变文件的长度,调用releaseblock函数将分配到的未使用的盘块进行释放,返回1

参数

参数原型说明
FCB* fcbp目录项指针
char* Buffer要写入的字符串

返回值

返回值说明
0磁盘空间不足,不能将信息写入文件
1成功写入

定义

int buffer_to_file(FCB* fcbp, char* Buffer)	//Buffer写入文件
{
	//成功写入文件,返回1;写文件失败,返回0

	short bn1, bn2, i, j, s, s0, len, size, count = 0;

	len = strlen(Buffer);	//取字符串Buffer长度
	s0 = s = fcbp->Addr;		//取文件首块号
	if (len == 0)
	{
		fcbp->Addr = fcbp->Fsize = 0;	//文件变为空文件
		releaseblock(s);			//释放文件占用的磁盘空间
		return 1;
	}
	size = fcbp->Fsize;	//取文件长度
	bn1 = len / SIZE + (short)(len%SIZE > 0);		//Buffer若存盘占用的盘块数
	bn2 = size / SIZE + (short)(size%SIZE > 0);	//文件原先内容占用的盘块数
	if (FAT[0] < bn1 - bn2)
	{
		cout << "\n磁盘空间不足,不能将信息写入文件。\n";
		return 0;
	}
	if (s == 0)				//若是空文件
	{
		s0 = s = getblock();	//为其分配首个盘块
		fcbp->Addr = s0;		//记下首块号
	}
	j = 0;
	while (j < len)		//Buffer写入FilName2
	{
		if (s < 0)
		{
			s = getblock();
			FAT[s0] = s;
		}
		for (i = 0; i < SIZE; i++, j++)
		{
			if (j == len)
				break;
			if (Buffer[j] == '\\' && Buffer[j + 1] == 'n')
			{
				Disk[s][i] = '\n';
				j++;
				count++;
			}
			else
				Disk[s][i] = Buffer[j];
		}
		s0 = s;
		s = FAT[s];
	}
	if (s > 0)
	{
		FAT[s0] = -1;			//目标文件结束盘块标记
		releaseblock(s);	//若FileName2仍有盘块未使用,应释放它们
	}
	fcbp->Fsize = len - count;		//改变文件的长度
	return 1;
}

releaseblock

解释

回收从盘块s开始链接的盘块链的磁盘空间

参数

参数原型说明
short s盘块号

返回值

返回值说明
void

定义

void releaseblock(short s)	//回收磁盘空间
{	//释放s开始的盘块链
	short s0;
	while (s > 0)				//循环操作,直到盘块链尾部
	{
		s0 = s;				//s0记下当前块号		
		s = FAT[s];			//s指向下一个盘块
		FAT[s0] = 0;			//释放盘块s0
		FAT[0]++;			//空闲盘块数增1
	}
}

file_to_buffer

解释

从盘块s开始,将盘块s所链接的盘块链中的字符串读入Buffer

参数

参数原型说明
FCB* fcbp目录项指针
char* Buffer要读出的字符串

返回值

返回值说明
非负值文件长度

定义

int file_to_buffer(FCB* fcbp, char* Buffer)	//文件内容读到Buffer,返回文件长度
{
	//文件内容读到Buffer,返回文件长度

	short s, len, i, j = 0;

	len = fcbp->Fsize;				//取文件长度
	s = fcbp->Addr;					//取文件首块号
	while (s > 0)
	{
		for (i = 0; i < SIZE; i++, j++)
		{
			if (j >= len)				//已读完该文件
				break;
			Buffer[j] = Disk[s][i];
		}
		s = FAT[s];					//取下一个盘块
	}
	Buffer[j] = '\0';
	return len;						//返回文件长度
}

PutUdtab

解释

udtab表中加入表项,代表要删除文件的信息

操作流程如下:

  • 计算要删除的文件占用的盘块数bn、存储要删除文件所有盘块号信息需要的盘块数m
  • 判断udtab表是否已满,若已满,则通过DelUd函数删除udtab表中的第1项,并将后续项前移
  • 判断FAT是否够用,若不够用,提示磁盘空间不足,不能保存删除恢复信息,该文件删除后将不能恢复并返回-1
  • 将目录项的相关信息存储在udtab表中,并调整“指针”Udelp位置,返回1

参数

参数原型说明
FCB* fcbp目录项指针
char* Buffer要读出的字符串

返回值

返回值说明
-1磁盘空间不足,只能彻底删除
1成功将被删除文件信息保存在udtab表中

定义

int PutUdtab(FCB *fp)
{
	//在udtab中加入一表项

	short bb, bn, n, m, size;
	size = fp->Fsize;
	bn = size / SIZE + (size%SIZE > 0) + 1;	//文件的盘块号个数(含-1)
	n = SIZE / sizeof(short);			//每个盘块可存储的盘块号数
	m = bn / n + (short)(bn%n > 0);			//共需m个盘块存储文件的块号
	if (Udelp == DM)
		Del1Ud(0);
	if (m > FAT[0])
	{
		cout << "\n磁盘空间不足,不能保存删除恢复信息,该文件删除后将不能恢复.\n";
		return -1;
	}
	strcpy(udtab[Udelp].gpath, temppath);
	strcpy(udtab[Udelp].ufname, fp->FileName);
	bb = udtab[Udelp].ufaddr = fp->Addr;
	udtab[Udelp].fb = SAVE_bn(bb);	//保存被删除文件的盘块号
	Udelp++;						//调整指针位置
	return 1;
}

Del1Ud

解释

删除udtab表中索引号为a的文件信息,前移其后续表项

参数

参数原型说明
short a要删除的udtab[a]的索引号a

返回值

返回值说明
void

定义

void Del1Ud(short a)
{
	// 在udtab表中删除一项,并前移后续表项

	short i, b, b0;
	b = udtab[a].fb;
	while (b > 0)
	{	//回收存储文件块号的磁盘空间
		b0 = b;
		b = FAT[b];
		FAT[b0] = 0;
		FAT[0]++;
	}
	for (i = a; i < Udelp - 1; i++)		//udtab表中表项前移一个位置
		udtab[i] = udtab[i + 1];
	Udelp--;
}

GetAttrib

解释

将由r | h | s组成的属性字符串转换成相应的属性字符,存入引用变量attrib中(主要通过按位或运算操作)

参数

参数原型说明
char* str属性字符串
char& attrib用来存放属性字符结果的引用变量

返回值

返回值说明
-1命令中属性参数错误
1解析成功

定义

int GetAttrib(char* str, char& attrib)
{
	int i, len;
	char ar = '\01', ah = '\02', as = '\04';
	if (str[0] != '|')
	{
		cout << "\n命令中属性参数错误。\n";
		return -1;
	}
	len = strlen(str);
	_strlwr(str);		//转换成小写字母
	for (i = 1; i < len; i++)
	{
		switch (str[i])
		{
		case 'r': attrib = attrib | ar;
			break;
		case 'h': attrib = attrib | ah;
			break;
		case 's': attrib = attrib | as;
			break;
		default: cout << "\n命令中属性参数错误。\n";
			return -1;
		}
	}
	return 1;
}

M_NewDir

解释

在指定盘块中新建目录

操作流程如下:

  • 通过getblock函数申请一块盘块,若无空闲盘块,则返回-1
  • 分配得盘块后,将该目录项进行初始化,并将盘块中的第1个目录项设为其父目录,返回其所在的盘块号

参数

参数原型说明
char* Name目录名字符串
short fs新目录的父目录首块号
char attrib新目录的属性

返回值

返回值说明
-1无空闲盘块
1新建目录成功

定义

int M_NewDir(char *Name, FCB* p, short fs, char attrib)	//在p位置创建一新子目录
{
	//成功返回新子目录的首块号

	short i, b, kk;
	FCB *q;
	kk = SIZE / sizeof(FCB);
	b = getblock();		//新目录须分配一磁盘块用于存储目录项“..”
	if (b < 0)
		return b;
	strcpy(p->FileName, Name);	//目录名
	p->Fattrib = attrib;			//目录项属性为目录而非文件
	p->Addr = b;					//该新目录的首块号
	p->Fsize = 0;					//子目录的长度约定为0
	q = (FCB*)Disk[b];
	for (i = 0; i < kk; i++, q++)
		q->FileName[0] = '\0';	//置空目录项标志*/
	q = (FCB*)Disk[b];
	strcpy(q->FileName, "..");	//新目录中的第一个目录项名是“..”
	q->Fattrib = (char)16;		//目录项属性为目录而非文件
	q->Addr = fs;					//该目录的首块号是父目录的首块号
	q->Fsize = 0;					//子目录的长度约定为0
	return b;					//成功创建,返回
}

save_FAT

解释

FAT表存入文件FAT2008.txt

操作流程如下:

  • 通过ofstream对象,将FAT表的数据以空格为分隔,写入文件FAT2008.txt

参数

参数原型说明
void

返回值

返回值说明
void

定义

void save_FAT()	//保存文件分配表FAT到磁盘文件FAT.txt
{
	int i;
	ofstream ffo;
	ffo.open("FAT2008.txt");
	for (i = 0; i < K; i++)
		ffo << FAT[i] << ' ';
	ffo.close();
}

save_Disk

解释

Disk表存入文件Disk2008.dat文件中

操作流程如下:

  • 将盘块分配指针ffbp、删除文件恢复表指针Udelp存入Disk[0]
  • 通过ofstream对象,以二进制方式,将Disk表的数据写入文件Disk2008.dat

参数

参数原型说明
void

返回值

返回值说明
void

定义

void save_Disk() //保存盘块中的文件内容
{
	int i;
	short *p = (short*)Disk[0];
	p[0] = ffbp;
	p[1] = Udelp;
	ofstream ffo("Disk2008.dat", ios::binary);
	for (i = 0; i < K; i++)
		ffo.write((char*)&Disk[i], SIZE);
	ffo.close();
}

save_UdTab

解释

将删除文件恢复表udtab表存入文件UdTab2008.dat文件中

操作流程如下:

  • 通过ofstream对象,以二进制方式,将udtab表的数据写入文件UdTab2008.dat

参数

参数原型说明
void

返回值

返回值说明
void

定义

void save_UdTab()	//保存被删除文件信息表
{
	int i;
	ofstream ffo("UdTab2008.dat", ios::binary);
	for (i = 0; i < DM; i++)
		ffo.write((char*)&udtab[i], sizeof(udtab[0]));
	ffo.close();
}

Udfile

解释

udtab表中查找路径相同、名字相同、首块号相同的表项,进行恢复

操作流程如下:

  • udtab表中寻找与参数路径相同、名字相同、首块号相同的表项
  • 找到后,询问用户是否恢复
    • 若用户的输入以y开头,则尝试恢复:
      • 若表项的目录项指针非空,则判断对应盘块是否空闲
        • 若已不空闲,说明不能恢复,通过Del1Ud函数删除udtab中该无用表项,提示文件XXX已不能恢复并返回-1
        • 若仍然空闲,从根目录开始,不断重新生成盘块链,通过FindFCB指针找到udtab表中相应表项的首块号,将文件名进行恢复
          • 若遇到重名文件的问题,需要重新输入文件名,直到合法可用为止
    • 每次恢复成功后,统计恢复文件个数,通过Del1Ud函数删除udtab表中对应表项
  • 操作完成后,返回0

参数

参数原型说明
FCB* fdp待恢复文件指针
short s0待恢复文件盘块号
char* fn待恢复文件文件名
short& cc存储恢复文件数的引用变量

返回值

返回值说明
-1无法恢复
0恢复成功

定义

int Udfile(FCB *fdp, short s0, char *fn, short &cc)
{
	// 在目录中找到被删除文件(文件名首字符为'\0xe5')的目录项后调用此函数
	// 本函数在udtab表中逐个查找,当找到与被删除文件的路径相同、名字(首字
	// 符除外)相同、首块号相同的表项时,显示“可能可以恢复字样”,询问用
	// 户得到肯定答复后,即开始恢复工作。恢复中若发现发生重名冲突时,由用
	// 户输入新文件名解决。恢复中若发现文件原先占用的盘块已作它用,则恢复
	// 失败。无论恢复成功与否,都将删除udtab中对应的表项。

	int i, j;
	char yn[11], Fname[INPUT_LEN];
	short *stp, b, b0, b1, s;
	FCB* fcbp;

	for (i = 0; i < Udelp; i++)
	{
		if (strcmp(udtab[i].gpath, temppath) == 0 && strcmp(&udtab[i].ufname[1], fn) == 0
			&& udtab[i].ufaddr == fdp->Addr)
		{
			cout << "\n文件" << udtab[i].ufname << "可能可以恢复,是否恢复它?(y/n) ";
			cin.getline(yn, 10);
			if (yn[0] == 'y' || yn[0] == 'Y')
			{
				if (udtab[i].ufaddr > 0)
				{
					b = udtab[i].fb;			//取存储被删文件盘块号的第一个块号
					stp = (short*)Disk[b];	//stp指向该盘块
					b0 = stp[0];				//取被删除文件的第一个块号到b0
					j = 1;
					while (b0 > 0)
					{
						if (FAT[b0] != 0)		//若被删除文件的盘块已经不空闲
						{
							cout << "\n文件" << udtab[i].ufname << "已不能恢复。\n";
							Del1Ud(i);		//删除udtab表中第i项(该表项已无用)
							return -1;
						}
						b0 = stp[j++];		//取被删除文件的下一个块号到b0
						if (j == SIZE / 2 && b0 != -1)
						{
							b = FAT[b];
							j = 0;
							stp = (short*)Disk[b];
						}
					}
					b = udtab[i].fb;
					stp = (short*)Disk[b];
					b0 = b1 = stp[0];
					j = 1;
					while (b1 > 0)
					{
						b1 = stp[j];
						FAT[b0] = b1;
						FAT[0]--;
						b0 = b1;
						j++;
						if (j == SIZE / 2 && b1 != -1)
						{
							b = FAT[b];
							j = 0;
							stp = (short*)Disk[b];
						}
					}
				}
				s = FindFCB(udtab[i].ufname, s0, '\0', fcbp);
				fdp->FileName[0] = udtab[i].ufname[0];	//恢复文件名
				if (s >= 0)	//有重名文件
				{
					cout << "\n该目录中已经存在名为" << udtab[i].ufname << "的文件,"
						<< "请为被恢复文件输入一个新的名字:";
					while (1)
					{
						cin.getline(Fname, INPUT_LEN);
						if (IsName(Fname))	//若输入的名字符合规则
						{
							s = FindFCB(Fname, s0, '\0', fcbp);	//查输入名字有否重名
							if (s >= 0)
								cout << "\n输入的文件名发生重名冲突。\n请重新输入文件名:";
							else
								break;			//输入名字合法且无重名文件存在。退出循环
						}
						else					//输入名字不符合命名规则
							cout << "\n输入的文件名不合法。\n请重新输入文件名:";
					}
					strcpy(fdp->FileName, Fname);
				}
				cc++;		//被恢复文件数增1
				Del1Ud(i);	//删除udtab表中第i项
			}
		}
	}
	return 0;
}

四、程序流程

1、初始化阶段

通用初始化1

定义变量char cmd[INPUT_LEN]作为命令行缓冲区,用来接收输入

初始化当前目录curpath的信息:

  • curpath.fblock = 1;——将当前目录的首块号设为根目录的首块号1
  • strcpy(curpath.cpath, "/");——将当前目录的路径字符串设为根目录的路径字符串/

def INIT

定义文件目录项指针FCB* fcbp

初始化FAT

设置FAT[1] ~ FAT[29]2 ~ 30,初始化根目录的FAT表

设置FAT[30]-1,根目录尾标记

设置FAT[31] ~ FAT[39]-1,各子目录尾标记

设置FAT[0]K-1 - 39

初始化Disk

文件目录项指针fcbp最初指向Disk[1]

要完成初始目录树(用到Disk[1]~Disk[39])的构建,先对Disk[1]~Disk[40]的160个文件目录项进行初始化:

  • 将这160个目录项设置文件名为空,代表空文件目录项

  • 初始化设置方法如下:

    fcbp指向某盘块(如Disk[1])的第一个目录项后,对每个目录项分别设置其文件属性、文件首块号、文件长度,然后通过fcbp++转到同一盘块下的下一个目录项位置,继续相应的设置

  • 具体设置如下:

    • Disk[1]

      fcbp = (FCB*)Disk[1];			//【根目录"/"】
      strcpy(fcbp->FileName, "bin");	//目录"/bin"
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 31;					//目录的首盘块号是31
      fcbp->Fsize = 0;					//目录的长度为0
      fcbp++;							//指向下一个目录项
      strcpy(fcbp->FileName, "usr");	//目录"/usr"
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 32;					//目录的首盘块号是32
      fcbp->Fsize = 0;					//目录的长度为0
      fcbp++;
      strcpy(fcbp->FileName, "auto");	//文件"/auto"
      fcbp->Fattrib = 0;					//普通文件
      fcbp->Addr = 0;						//文件的首盘块号是0,表示是空文件
      fcbp->Fsize = 0;					//该文件的长度为0
      fcbp++;
      strcpy(fcbp->FileName, "dev");	//目录"/dev"
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 33;					//目录的首盘块号是33
      fcbp->Fsize = 0;					//目录的长度为0
      
    • Disk[31]

      fcbp = (FCB*)Disk[31];			//【目录"/bin/"】
      strcpy(fcbp->FileName, "..");	//目录"/bin/.."(不规范的表示)
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 1;						//父目录的首盘块号是1(根目录)
      fcbp->Fsize = 0;					//目录的长度为0
      
    • Disk[32]

      fcbp = (FCB*)Disk[32];			//【目录"/usr/"】
      strcpy(fcbp->FileName, "..");	//目录"/usr/.."(不规范的表示)
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 1;						//父目录的首盘块号是1(根目录)
      fcbp->Fsize = 0;					//目录的长度为0
      fcbp++;
      strcpy(fcbp->FileName, "user");	//目录"/usr/user" 
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 34;					//目录的首盘块号是34
      fcbp->Fsize = 0;					//目录的长度为0
      fcbp++;
      strcpy(fcbp->FileName, "lib");	//目录"/usr/lib"
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 35;					//目录的首盘块号是35
      fcbp->Fsize = 0;					//目录的长度为0
      fcbp++;
      strcpy(fcbp->FileName, "bin");	//目录"/usr/bin"
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 36;					//目录的首盘块号是36
      fcbp->Fsize = 0;					//目录的长度为0
      
    • Disk[33]

      fcbp = (FCB*)Disk[33];			//【目录"/dev/"】
      strcpy(fcbp->FileName, "..");	//目录"/dev/.."(不规范的表示)
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 1;						//父目录的首盘块号是1(根目录)
      fcbp->Fsize = 0;					//目录的长度为0
      
    • Disk[34]

      fcbp = (FCB*)Disk[34];			//【目录"/usr/user/"】
      strcpy(fcbp->FileName, "..");	//目录"/usr/user/.."(不规范的表示)
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 32;					//父目录的首盘块号是32(usr目录)
      fcbp->Fsize = 0;					//目录的长度为0
      fcbp++;
      strcpy(fcbp->FileName, "li");	//目录"/usr/user/li"
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 37;					//目录的首盘块号是37
      fcbp->Fsize = 0;					//目录的长度为0
      fcbp++;
      strcpy(fcbp->FileName, "sun");	//目录"/usr/user/sun"
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 38;					//目录的首盘块号是38
      fcbp->Fsize = 0;					//目录的长度为0
      fcbp++;
      strcpy(fcbp->FileName, "ma");	//目录"/usr/user/ma"
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 39;					//目录的首盘块号是39
      fcbp->Fsize = 0;					//目录的长度为0
      
    • Disk[35]

      fcbp = (FCB*)Disk[35];			//【目录"/usr/lib/"】
      strcpy(fcbp->FileName, "..");	//目录"/usr/lib/.."(不规范的表示)
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 32;					//父目录的首盘块号是32(usr目录)
      fcbp->Fsize = 0;					//目录的长度为0
      
    • Disk[36]

      fcbp = (FCB*)Disk[36];			//【目录"/usr/bin/"】
      strcpy(fcbp->FileName, "..");	//目录"/usr/bin/.."(不规范的表示)
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 32;					//父目录的首盘块号是32(usr目录)
      fcbp->Fsize = 0;					//目录的长度为0
      
    • Disk[37]

      fcbp = (FCB*)Disk[37];			//【目录"/usr/user/li/"】
      strcpy(fcbp->FileName, "..");	//目录"/usr/user/li/.."(不规范的表示)
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 34;					//父目录的首盘块号是34(usr/user目录)
      fcbp->Fsize = 0;					//目录的长度为0
      
    • Disk[38]

      fcbp = (FCB*)Disk[38];			//【目录"/usr/user/sun/"】
      strcpy(fcbp->FileName, "..");	//目录"/usr/user/sun/.."(不规范的表示)
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 34;					//父目录的首盘块号是34(usr/user目录)
      fcbp->Fsize = 0;					//目录的长度为0
      
    • Disk[39]

      fcbp = (FCB*)Disk[39];			//【目录"/usr/user/ma/"】
      strcpy(fcbp->FileName, "..");	//目录"/usr/user/ma/.."(不规范的表示)
      fcbp->Fattrib = 16;					//目录
      fcbp->Addr = 34;					//父目录的首盘块号是34(usr/user目录)
      fcbp->Fsize = 0;					//目录的长度为0
      
    image-20201231082711441 image-20201127160230855
初始化Udelp、ffbp

设置Udelp = 0,表示被删除文件恢复表为空

设置ffbp = 1,表示从FAT表开头查找空闲盘块

undef INIT

文件初始化FAT

通过ifstream对象从文件FAT2008.txt中读取文件分配表FATK个值并存入

文件初始化Disk

通过ifstream对象从文件Disk2008.dat中(二进制方式)读取磁盘空间Disk的内容,一次读取SIZE个字节到Disk[i]中,读取K

文件初始化udtab

通过ifstream对象从文件UdTab2008.dat中(二进制方式)读取删除文件恢复表udtab的内容,一次读取sizeof(UnDel)个字节到udtab[i],读取DM

初始化Udelp、ffbp

设置Udelp = Disk[0][0]ffbp = Disk[0][1],表示从Disk[0]中读取Udelpffbp

通用初始化2

初始化uof

设置uof各项的state = 0,表示系统初始时并没有文件被打开

输出提示

输出如下的提示信息

现在你可以输入各种操作命令.
Help —— 简易帮助信息.
exit —— 退出本程序.

2、交互阶段

主循环

在交互阶段中,最外层有一个主循环

主循环内包含以下操作:

  • 命令输入循环
  • 分析命令
  • 执行命令

命令输入循环

在命令输入循环中,重复以下操作:

  • 输出当前路径
  • 等待用户指令

循环的条件是命令不为空

分析命令

通过函数ParseCommand分析用户输入的命令cmd,获得命令参数个数k

执行命令

通过函数ExecComd,结合参数k以及全局变量中分析出的命令-参数表comd,尝试执行相关命令

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

God-Excious

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值