<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
六、支持长文件名(一)。
1、代码页的功能
(1)在原来的程序中,我把 #define _CODE_PAGE 936,代码页定义为936.
这时候可以在磁盘上新建中文名称的文件夹和文件,(通过fwrite命令实现,只要数目小于4个就行,也可以用fread命令读出来),也可以显示中文文件名。(通过命令flist来实现)。
(2)我把 #define _CODE_PAGE 1,代码页定义为1后,调用flist能正常显示中文名。但是调用fread和fwrite命令不能读取和创建中文名文件了。
进入代码分析,显示时关键在函数 get_fileinfo(),它原封不动的将系统标准名 转换为一般字符串,对字符串不做任何处理,送到串口以后自然可以正常显示。
但是读取文件和写入文件时,首先调用f_open()函数,进入follow_path()函数,然后调用create_name(),当它遇到大于 0x80 的字符时,产生如下效果:
if (c >= 0x80) { /* Extended char */
#ifdef _EXCVT
c = cvt[c - 0x80]; /* Convert extend char (SBCS) */
#else
b |= 3; /* Eliminate NT flag if ext char is exist */
#if !_DF1S /* ASCII only cfg */
return FR_INVALID_NAME;
#endif
#endif
}
返回FR_INVALID_NAME,所以它不识别中文GB2312(OEM)代码。
如果定义了define _CODE_PAGE==936,则同时就会定义DF1S,也同时定义了IsDBCS1(c)和IsDBCS2(d)。因此可以将中文OEM代码转换为标准文件名,存储于文件系统的目录项里面。
2、如果定义长文件名_USE_LFN,而不定义UNICODE码。有什么效果呢?
根据要求,先要添加ff_convert() and ff_wtoupper()两个函数。
结果发现,光两个转换表就要150K左右,我这个128k的空间是远远不够了。只能看代码而不能测试了。不定义UNICODE码的含义是用户提供的路径名不是以UNICODE形式出现的。
以下,就只能纸上谈兵了。
3、如果0:/doc 目录下有一个文件名 (这是一个very long的文件.它里面是空的.txt文档)。
如果要显示这个目录下所有的文件信息,我会在命令界面上输入:
Flist 0:/doc。
执行这个命令,程序首先调用
F_opendir(DIR dj,const void *path)函数,对输入的路径进行分析。这个函数首先调用函数 chk_mounted()检查磁盘上是否有文件系统,如果已经取得了信息,则很快返回。如果没有,则初始化磁盘,并读取信息,填充文件系统信息结构体FATFS。它去掉了路径前面的 0:。
F_opendir接下来调用follow_path(dj, path),这个函数根据路径填充目录信息结构体。这个函数首先设置dj->sclust,如果前面有/,会去掉这个符号。接下来一直读取目录名直到下一个/。这个工作调用create_name(dj, &path)来完成。
进入create_name(dj, &path),首先做的工作是根据路径填充dj->lfn指向的内存单元,由于我的这个目录是短文件名:doc,所以填入缓冲区的是 ‘d0‘,’o0‘,’c0‘,由于目录到此结束,所以NS_LAST属性被设置,表明已经到路径末尾。同时lfn[di] = 0,di此时指向c0后面那个内存单元。注意,存储进长目录区是以unicode的形式,而path用的是ANSi码,汉字两个字节,英文字母一个字节。
create_name(dj, &path)继续执行,mem_set(dj->fn, ' ', 11),先将dj结构体的标准短文件名填充空格。if (si) cf |= NS_LOSS | NS_LFN通过这句话的判断,长文件名前面没有空格和.,没有超出8.3格式。当然后面还要继续判断。执行while (di && lfn[di - 1] != '.') di—这句后,di=0,表示没有找到扩展名(doc没有扩展名)。dj->fn[i++] = (BYTE)w,通过这个方式将doc存进了短文件名存储区。dj->fn[NS] = cf,短文件名存储区12个字节,前11个是标准短文件名,最后一个是路径属性字节:是否.目录、大小写标志,超出8.3格式,是否长文件名、是否路径结束标志等等。
所以create_name(dj, &path)的工作主要是根据路径填充dj->lfn和dj->fn,前者每个字符16Byte,以‘\0’结尾,后者12个字节,以路径的整体属性结尾。
执行路径返回follow_path(dj, path),接下来调用dir_find(dj),这个函数的作用是在当前目录(对应dj->sclust)下,搜索dj->lfn和dj->fn对应的目录项,找到以后让dj->index和dj->dir指向该目录项的数据。追踪那个进入该函数。
在函数dir_find(dj)里,首先dir_seek(dj, 0),将索引定位于0,目录第一项。ord = sum = 0xFF长目录项索引和校验和设为-1。c = dir[DIR_Name]取出目录项的第一个字符进行判断,a = dir[DIR_Attr] & AM_MASK取出属性进行判断。建立一个循环,不断移动目录缓冲(每次下移32个字节),通过dir_next(dj, FALSE)来实现。当找到”doc “目录项时,if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break跳出循环,此时的有效信息就是dj->sclust加上dj->index和dj->dir,它指向根目录里的“doc”目录项。
执行路径返回follow_path(dj, path),由于last = *(dj->fn+NS) & NS_LAST,标志被置位,很快跳出路径分解的循环,返回到F_opendir(DIR dj,const void *path)。通过下面这个计算:
dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO)真正得到了分配给doc目录的起始簇的地址。接下来就可以读取 0:/doc 目录下所有的目录项,并显示它包含文件、目录的属性信息了。
回到主程序执行路径,接下来调用:
f_readdir()这个函数功能是根据DIR结构体提供的信息,填充文件属性结构体,包括文件名字符串、文件大小、文件修改日期等。进入该函数进行追踪。这里就快要找到我的长文件名目录项《这是一个very long的文件.它里面是空的.txt文档》了,它在目录项里是怎样的存在形式呢?
它共有29个字(包括空格和.),共占据3个长目录项和一个短目录项:其格式应该如下。
43 | t文档 | 0000H | FFFFH | 0FH | 00H | Num | FFFFH | 长文件名 | |||||
| FFFFH FFFFH FFFFH FFFFH | 0000 | FFFFH | FFFFH | |||||||||
02 | 的文件.它 | 0FH | 00H | Num | 里 | ||||||||
面是空的. | 0000 | tx | |||||||||||
01 | 这是一个v | 0FH | 00H | Num | e | ||||||||
ry lo | 0000 | ng | |||||||||||
这 | 是 | 一 | ~ | 1 | T | X | T | 20H | NT | XX | 创建时间 | 短文件名 | |
创建日期 | 访问日期 | 起始簇号高位 | 修改时间 | 修改日期 | 起始簇号低位 | 文件的大小 | |||||||
f_readdir()这个函数调用dir_read()函数,读取各个目录项的信息,然后调用get_fileinfo()函数得到文件的各项信息。下面进入dir_read()进行跟踪,看它是怎样对待长目录项的。
进入dir_read()函数,首先ord, sum = 0xFF,读取属性a = dir[DIR_Attr] & AM_MASK,if (a == AM_LFN),也就是当遇到长目录索引“43”的时候,sum = dir[LDIR_Chksum];c &= 0xBF; ord = c; dj->lfn_idx = dj->index;取得校验和,取得索引(这里是3),使长目录索引指向“43”目录项的索引号。
如果pick_lfn(dj->lfn, dir)返回TRUE,则索引自动ord=ord-1变为2。进入pick_lfn(dj->lfn, dir),它的作用是将当前目录项里的长文件名部分写入dj->lfn指向的名字缓冲区。先从偏移26开始存入“t文档”三个unicode字符,然后写入0,返回TRUE。也就是ord=2。
继续执行,dir_next(dj, FALSE),dj->index指向下一项,a = dir[DIR_Attr] & AM_MASK,重新取得属性。此时c = dir[DIR_Name]=02,满足(c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir))这个表达式,所以索引又被设置为ord=1。
同上,再次读取ord=1的那一项,填充长文件名缓冲区,至此unicode名称已经完全存入dj->lfn指向的缓冲区,并且ord=0,表明长目录项已经结束。
通过dir_next()再次循环,接下来dj->index指向对应的短目录项,只要sum_sfn(dir)计算出来的校验和与前面长目录项里取得的校验和相等,dir_read()函数的任务就算完成了。它获得的信息包括:dj->lfnidx(长目录项开始索引),dj->lfn(完整的unicode长文件名),dj->index和dj->dir指向短目录项。执行路径回到f_readdir()。
f_readdir()函数接下来调用get_fileinfo(dj, fno)获取目录项的详细信息。追踪该函数的执行。这个函数共获取六个信息:大小、属性、修改时间、日期、短文件名、长文件名字符串(通过读取dj->lfn指向的缓冲区,并将unicode转换为OEM代码)。有了这个文件属性结构体的数据,用户就可以在串口终端显示目录下所有目录项的信息了。
用户219698 2010-4-25 12:57
nthq2004 2010-4-25 09:29
nthq2004 2010-4-25 09:25
用户219698 2010-4-24 23:03
nthq2004 2010-4-22 22:37
用户219698 2010-4-22 22:01
nthq2004 2010-4-22 12:22
用户219698 2010-4-21 21:52