Работа с длинными именами файлов из под DOS

Работа с длинными именами файлов из под DOS

Работа с длинными именами файлов из под DOS

В этом модуле реализованы функции FindFirst и FindNext, но не простые, а работающие с длинными именами файлов под ГОЛЫМ ДОСОМ. Формат длинных имен, насколько я помню, узнавался методом высоконаучного тыка, так что никаких гарантий.

Писал очень давно, и сейчас даже откомпилировать не смог, так как не нашел модуля uni2dos. Зато вспомнил, что он делает :) Он перекодирует строку из Unicode в DOS кодировку. Таблица Unicodе для русских символов приведена тут.

lfn.pas

  1. uses uni2dos;
  2.  
  3. type TStr3=Array[1..3] of Char;
  4. TStr8=Array[1..8] of Char;
  5. TStr10=Array[1..10] of Char;
  6.  
  7. type TSector0 = Record
  8. Jump : Array[1..3] of Char; { NEAR-переход на код загрузки }
  9. OEMName : Array[1..8] of Char; { OEM-имя компании и версия системы }
  10. SectSize : Word; { байт на сектор }
  11. ClustSize : Byte; { секторов на единицу распределения (кластер) }
  12. ResSecs : Word; { резервных секторов (секторов перед первой FAT) }
  13. FatCnt : Byte; { число таблиц FAT }
  14. RootSiz : Word; { макс.число 32-байтовых элементов корневого оглавления }
  15. TotSecs : Word; { общее число секторов на носителе (раздел DOS) }
  16. Media : Byte; { дескриптор носителя (то же, что 1-й байт FAT) }
  17. FatSize : Word; { число секторов в одной FAT }
  18. TrkSecs : Word; { секторов на дорожку (цилиндр) }
  19. HeadCnt : Word; { число головок чтения/записи (поверхностей) }
  20. HidnSec : Word; { спрятанных секторов (исп. в схемах разделения) }
  21. Ostatok : Array[1..486] of Char; { Остаток, что-бы сюда можно было читать сектор }
  22. End;
  23.  
  24. type TFile = Record
  25. Name : TStr8; { Имя файла }
  26. Ext : TStr3; { Расширение }
  27. Attr : Byte; { Атрибут файла }
  28. Reserv : TStr10; { Резерв }
  29. Time : Word; { время создания/модификации в формате filetime }
  30. Date : Word; { дата создания/модификации в формате filetime }
  31. ClstrNo : Word; { номер начального кластера данных (связь с FAT ) }
  32. Size : LongInt; { размер файла в байтах }
  33. End;
  34.  
  35. type TLongSearchRec = Record
  36. Name : TStr8; { Имя файла }
  37. LongName : String; { Длиное имя файла }
  38. Ext : TStr3; { Расширение }
  39. Attr : Byte; { Атрибут файла }
  40. Reserv : TStr10; { Резерв }
  41. Time : Word; { время создания/модификации в формате filetime }
  42. Date : Word; { дата создания/модификации в формате filetime }
  43. ClstrNo : Word; { номер начального кластера данных (связь с FAT ) }
  44. Size : LongInt; { размер файла в байтах }
  45. FPosInSec : Integer; { Позиция - в секторе }
  46. FSecPos : Integer; { Позиция - сектор }
  47. FMask : String; { Маска для поиска }
  48. FAttr : Byte; { Путь заданный в FindFirst }
  49. End;
  50.  
  51. type TDTA = Record
  52. Reserved : Array[1..$15] of Byte; { Зарезервированно для FindNext }
  53. Attr : Byte; { Атрибут файла }
  54. Time : Word; { время создания/модификации в формате filetime }
  55. Date : Word; { дата создания/модификации в формате filetime }
  56. Size : LongInt; { размер файла в байтах }
  57. Name : Array[1..13] of Char; { Имя файла + . +расширение }
  58. End;
  59.  
  60. type TDir=Array[0..15] of TFile;
  61.  
  62. var s0 : TSector0;
  63. dir : TDir;
  64. Root : Word;
  65. I : Word;
  66. Search : TLongSearchRec;
  67. LongDosError : Integer;
  68. SecPerCat : Integer;
  69. DTA : TDTA; { Текущая DTA }
  70.  
  71. Function ReadSector(A:Pointer;Disk:Byte;Count:Word;Sector:Word):Boolean;
  72. var Error :Word;
  73. Offs :Word;
  74. Begin
  75. Offs:=Word(A);
  76. Error:=0;
  77. asm
  78. mov al,[Disk] {Номер диска 0-A}
  79. mov cx,[Count] {Счетчик считываемых символов}
  80. mov dx,[Sector] {Начальный сектор}
  81.  
  82. mov bx,[Offs]
  83. int 25h
  84. pop dx
  85. je @nError
  86. mov ax,1
  87. mov [Error],ax
  88. @nError:
  89. end;
  90. if Error=0 then ReadSector:=True
  91. else ReadSector:=False;
  92. End;
  93.  
  94. { Получаем длиное имя файла #################################################}
  95. function GetUniLFN(I:Word):String;
  96. var Str,Str0,Str1:String;
  97. Flag:Boolean;
  98. Len:Integer;
  99. Begin
  100. Flag:=true;Str0:=#255+#255;Str:='';Str1:='';
  101. While flag do
  102. Begin
  103. Str1:=copy(Dir[I-1].Name,2,7)+Dir[I-1].Ext+copy(Dir[I-1].Reserv,3,8)+char(Dir[I-1].Time)+
  104. char(Dir[I-1].Time shr 8)+char(Dir[I-1].Date)+char(Dir[I-1].Date shr 8)+char(Dir[I-1].Size)+
  105. char(Dir[I-1].Size shr 8)+char(Dir[I-1].Size shr 16)+char(Dir[I-1].Size shr 24);
  106. Len:=pos(Str0,Str1);
  107. If Len<>0 then Begin Flag:=False; Str:=Str+copy(Str1,1,Len); End
  108. else Begin
  109. dec(i);
  110. Str:=Str+Str1;
  111. if Dir[I].Attr<>15 then Flag:=False;
  112. End;
  113. End;
  114. GetUniLFN:=Str;
  115. End;
  116.  
  117. { Long Find First ###########################################################}
  118. Procedure LongFindFirst(Path:String;Attr:Word;var SR:TLongSearchRec);
  119. var I,Sec :Integer;
  120. Drive :Word; { Номер диска }
  121. NameSeg,NameOfs :Word; { Адрес строки для поиска }
  122. ParentDir :String; { Родительский каталог }
  123. Sector,InSect,Data :LongInt; { Сектор, и позиция каталоговой записи }
  124. DTASeg,DTASize,DTAOfs :Word; { Адрес локальгого DTA }
  125. Begin
  126. LongDOSError:=0;
  127. {Drive:=Path[1];} Drive:=0; { Получаем имя диска }
  128. ParentDir:=copy(Path,4,Length(Path)-1); { Выделим родительский каталог }
  129. If not ReadSector(@s0,Drive,1,0) then Exit;
  130. If Pos('\',ParentDir)=0 then
  131. Begin { Если ищем в корне #################################################}
  132. Path:=Path+#0;
  133. NameSeg:=Seg(Path);NameOfs:=Ofs(Path)+1;
  134. DTAOfs:=Ofs(DTA);DTASeg:=Seg(DTA);DTASize:=Sizeof(DTA);
  135. asm { FindFirst - заполняем }
  136. push ds
  137.  
  138. mov ax,[NameSeg]
  139. mov ds,ax
  140. mov dx,[NameOfs]
  141. mov ah,$4E { Функция DOS - найти первый файл }
  142. mov cx,Attr { Аттрибуты файла }
  143. int 21h
  144. jnc @FilesF
  145. mov ax,$FF
  146. mov [LongDOSError],ax
  147. jmp @AsmEnd
  148. @FilesF: mov ah,$2f { Получим адрес текущей DTA }
  149. int 21h
  150. mov ax,es { И скопируем ее в нашу локальную DTA }
  151. mov ds,ax
  152. mov si,bx
  153. mov di,[DTAOfs]
  154. mov es,[DTASeg]
  155. mov cx,[DTASize]
  156. cld
  157. rep movsb
  158.  
  159. @AsmEnd: pop ds
  160. end; { В DTA.Reserved[14] можно узнать номер найденой записи в каталоге}
  161. If LongDOSError<>0 then Exit;
  162. Sector:=trunc((DTA.Reserved[14]+256*DTA.Reserved[15])/(s0.SectSize shr 5)); { Номер сектора,где находится каталог с именем }
  163. InSect:=(DTA.Reserved[14]+256*DTA.Reserved[15])-Sector*(s0.SectSize shr 5); { Номер записи в секторе }
  164. Root:=s0.ResSecs+s0.FatSize*s0.FatCnt; { Вычисляем номер сектора с корнем }
  165. If not ReadSector(@dir,0,1,Root+Sector) then Exit; { Читаем сектор }
  166. SR.Name:=dir[InSect].Name; { Заполнаяем TLongSearchRec }
  167. SR.Ext:=dir[InSect].Ext;
  168. SR.Attr:=dir[InSect].Attr;
  169. SR.Reserv:=dir[InSect].Reserv;
  170. SR.Time:=dir[InSect].Time;
  171. SR.Date:=dir[InSect].Date;
  172. SR.ClstrNo:=dir[InSect].ClstrNo;
  173. SR.Size:=dir[InSect].Size;
  174. { Теперь смотрим длиное имя }
  175. If (InSect>0) and (dir[InSect-1].attr=15) then SR.LongName:=UniToDos(GetUniLFN(InSect))
  176. else SR.LongName:=dir[InSect].Name+'.'+dir[InSect].Ext;
  177. End
  178. else
  179. Begin { Если ищем не в корне ##############################################}
  180. Path:=Path+#0;
  181. NameSeg:=Seg(Path);NameOfs:=Ofs(Path)+1;
  182. DTAOfs:=Ofs(DTA);DTASeg:=Seg(DTA);DTASize:=Sizeof(DTA);
  183. asm { FindFirst - заполняем }
  184. push ds
  185.  
  186. mov ax,[NameSeg]
  187. mov ds,ax
  188. mov dx,[NameOfs]
  189. mov ah,$4E { Функция DOS - найти первый файл }
  190. mov cx,Attr { Аттрибуты файла }
  191. int 21h
  192. jnc @FilesF
  193. mov ax,$FF
  194. mov [LongDOSError],ax
  195. jmp @AsmEnd
  196. @FilesF: mov ah,$2f { Получим адрес текущей DTA }
  197. int 21h
  198. mov ax,es { И скопируем ее в нашу локальную DTA }
  199. mov ds,ax
  200. mov si,bx
  201. mov di,[DTAOfs]
  202. mov es,[DTASeg]
  203. mov cx,[DTASize]
  204. cld
  205. rep movsb
  206.  
  207. @AsmEnd: pop ds
  208. end; { В DTA.Reserved[14] можно узнать номер найденой записи в каталоге}
  209. If LongDOSError<>0 then Exit;
  210. Sector:=trunc((DTA.Reserved[14]+256*DTA.Reserved[15])/(s0.SectSize shr 5));
  211. InSect:=(DTA.Reserved[14]+256*DTA.Reserved[15])-Sector*(s0.SectSize shr 5); { Номер записи в секторе }
  212. Sector:=Sector+DTA.Reserved[16]+256*DTA.Reserved[17]-3; { Номер сектора,где находится каталог с именем }
  213. Root:=s0.ResSecs+s0.FatSize*s0.FatCnt; { Вычисляем номер сектора с корнем }
  214. Data:=Root+(s0.RootSiz*32) div s0.SectSize;
  215. If not ReadSector(@dir,0,1,Data+Sector) then Exit; { Читаем сектор }
  216. SR.Name:=dir[InSect].Name; { Заполнаяем TLongSearchRec }
  217. SR.Ext:=dir[InSect].Ext;
  218. SR.Attr:=dir[InSect].Attr;
  219. SR.Reserv:=dir[InSect].Reserv;
  220. SR.Time:=dir[InSect].Time;
  221. SR.Date:=dir[InSect].Date;
  222. SR.ClstrNo:=dir[InSect].ClstrNo;
  223. SR.Size:=dir[InSect].Size;
  224. { Теперь смотрим длиное имя }
  225. If (InSect>0) and (dir[InSect-1].attr=15) then SR.LongName:=UniToDos(GetUniLFN(InSect))
  226. else SR.LongName:=dir[InSect].Name+'.'+dir[InSect].Ext;
  227. End;
  228. End;
  229.  
  230. { Long Find Next ############################################################}
  231. Procedure LongFindNext(var SR:TLongSearchRec);
  232. var
  233. Drive :Word; { Номер диска }
  234. Sector,InSect,Data :LongInt; { Сектор, и позиция каталоговой записи }
  235. DTASeg,DTASize,DTAOfs :Word; { Адрес локальгого DTA }
  236. Begin
  237. Drive:=0;
  238. LongDOSError:=0;
  239. DTAOfs:=Ofs(DTA);DTASeg:=Seg(DTA);DTASize:=Sizeof(DTA);
  240. asm { FindFirst - заполняем }
  241. push ds
  242.  
  243. mov ah,$2f { Получим адрес текущей DTA }
  244. int 21h
  245. mov dx,bx
  246. mov ah,$4F { Функция DOS - найти первый файл }
  247. int 21h
  248. jnc @FilesF
  249. mov ax,$FF
  250. mov [LongDOSError],ax
  251. jmp @AsmEnd
  252. @FilesF: mov ah,$2f { Получим адрес текущей DTA }
  253. int 21h
  254. mov ax,es { И скопируем ее в нашу локальную DTA }
  255. mov ds,ax
  256. mov si,bx
  257. mov di,[DTAOfs]
  258. mov es,[DTASeg]
  259. mov cx,[DTASize]
  260. cld
  261. rep movsb
  262.  
  263. @AsmEnd: pop ds
  264. end; { В DTA.Reserved[14] можно узнать номер найденой записи в каталоге}
  265. If LongDOSError<>0 then Exit;
  266. Sector:=trunc((DTA.Reserved[14]+256*DTA.Reserved[15])/(s0.SectSize shr 5));
  267. InSect:=(DTA.Reserved[14]+256*DTA.Reserved[15])-Sector*(s0.SectSize shr 5); { Номер записи в секторе }
  268. Sector:=Sector+DTA.Reserved[16]+256*DTA.Reserved[17]-3; { Номер сектора,где находится каталог с именем }
  269. Root:=s0.ResSecs+s0.FatSize*s0.FatCnt; { Вычисляем номер сектора с корнем }
  270. Data:=Root+(s0.RootSiz*32) div s0.SectSize;
  271. If not ReadSector(@dir,0,1,Data) then Exit; { Читаем сектор }
  272. SR.Name:=dir[InSect].Name; { Заполнаяем TLongSearchRec }
  273. SR.Ext:=dir[InSect].Ext;
  274. SR.Attr:=dir[InSect].Attr;
  275. SR.Reserv:=dir[InSect].Reserv;
  276. SR.Time:=dir[InSect].Time;
  277. SR.Date:=dir[InSect].Date;
  278. SR.ClstrNo:=dir[InSect].ClstrNo;
  279. SR.Size:=dir[InSect].Size;
  280. { Теперь смотрим длиное имя }
  281. If (InSect>0) and (dir[InSect-1].attr=15) then SR.LongName:=UniToDos(GetUniLFN(InSect))
  282. else SR.LongName:=dir[InSect].Name+'.'+dir[InSect].Ext;
  283. End;
  284.  
  285. Begin
  286. WriteLn;
  287. InitUni;
  288. LongFindFirst('A:\1\*.*',$FFFF,Search);
  289. While LongDosError=0 do
  290. Begin
  291. WriteLn(Search.Name+' '+Search.Ext+' ',Search.Size:8,' ',
  292. Search.FPosInSec:8,' ',Search.FSecPos:8,' ',Search.LongName);
  293. LongFindNext(Search);
  294. Search.LongName:=copy(Search.LongName,1,30);
  295. End;
  296. End.

Download this code: lfn.pas

Комментарии