注:前言、目录见 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} ⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧文件名文件属性文件首块号文件长度2∼11字节(包括′\0′)1字节⎩⎪⎨⎪⎧016其他表示文件表示目录详见文件属性图2字节2字节
文件属性如下图
原型
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} ⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧文件全路径名文件属性文件的首块号文件大小(字节数)目录项指针打开状态读指针读写指针{01读写只读⎩⎪⎨⎪⎧012空表项新建打开{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
- 根据
temppath
和FileName
构造全路径名到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 | 新文件的文件属性有误 |
-4 | uof 表已满,不能创建新文件 |
-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
- 根据
temppath
和FileName
构造全路径名到gFilePath
中 - 通过
Check_UOF
函数判断该文件是否已经打开:- 若已打开,则提示
文件XXX原先已打开
并返回-5
- 若未打开,则尝试在
uof
表中寻找空表项:- 若
uof
已满,则提示UOF已满,不能打开文件
并返回-4
- 若
uof
未满,则通过Put_UOF
函数将该文件放入uof
表,提示文件XXX打开成功
并返回1
- 若
- 若已打开,则提示
命令
命令格式 | 命令说明 |
---|---|
open <文件名> | 打开指定文件 |
参数
参数原型 | 说明 |
---|---|
int k | 命令参数个数 |
返回值
返回值 | 说明 |
---|---|
-1 | 命令参数个数有误 |
-2 | 文件不存在 |
-3 | 路径名有误 |
-4 | uof 表已满 |
-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
- 根据
temppath
和FileName
构造全路径名到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.cpath
和curpath.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
- 个数为
1
或2
,则开始解析路径字符串,得到盘块号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
- 若未查找到,则提示
- 根据
temppath
和FileName
构造全路径名到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
- 若未查找到,则提示
- 根据
temppath
和FileName
构造全路径名到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
- 若未查找到,则提示
- 根据
temppath
和FileName
构造全路径名到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_FAT
、save_Disk
、save_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.cpath
和curpath.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
:说明是在当前目录下创建文件,令temppath
为curpath.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>0参数个数k任意参数个数k=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+1
(ffbp == 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
解释
根据参数gFileName
、status
、fcbp
构造相关的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 | 文件的全路径名 |
返回值
返回值 | 说明 |
---|---|
小于S 的i | 找到指定文件,位于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
结构中
- 空间不足,提示
- 为空,则将文件设置为空文件,调用``buffer_to_file
- 将长度为
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
-
初始化Udelp、ffbp
设置Udelp = 0
,表示被删除文件恢复表为空
设置ffbp = 1
,表示从FAT表开头查找空闲盘块
undef INIT
文件初始化FAT
通过ifstream
对象从文件FAT2008.txt
中读取文件分配表FAT
的K
个值并存入
文件初始化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]
中读取Udelp
和ffbp
通用初始化2
初始化uof
设置uof
各项的state = 0
,表示系统初始时并没有文件被打开
输出提示
输出如下的提示信息
现在你可以输入各种操作命令.
Help —— 简易帮助信息.
exit —— 退出本程序.
2、交互阶段
主循环
在交互阶段中,最外层有一个主循环
主循环内包含以下操作:
- 命令输入循环
- 分析命令
- 执行命令
命令输入循环
在命令输入循环中,重复以下操作:
- 输出当前路径
- 等待用户指令
循环的条件是命令不为空
分析命令
通过函数ParseCommand
分析用户输入的命令cmd
,获得命令参数个数k
执行命令
通过函数ExecComd
,结合参数k
以及全局变量中分析出的命令-参数表comd
,尝试执行相关命令