Работа с длинными именами файлов из под DOS
Комментариев:
В этом модуле реализованы функции FindFirst и FindNext, но не простые, а работающие с длинными именами файлов под ГОЛЫМ ДОСОМ. Формат длинных имен, насколько я помню, узнавался методом высоконаучного тыка, так что никаких гарантий.
Писал очень давно, и сейчас даже откомпилировать не смог, так как не нашел модуля uni2dos. Зато вспомнил, что он делает :) Он перекодирует строку из Unicode в DOS кодировку. Таблица Unicodе для русских символов приведена тут.
lfn.pas
uses uni2dos;type TStr3=Array[1..3] of Char;TStr8=Array[1..8] of Char;TStr10=Array[1..10] of Char;type TSector0 = RecordJump : Array[1..3] of Char; { NEAR-переход на код загрузки }OEMName : Array[1..8] of Char; { OEM-имя компании и версия системы }SectSize : Word; { байт на сектор }ClustSize : Byte; { секторов на единицу распределения (кластер) }ResSecs : Word; { резервных секторов (секторов перед первой FAT) }FatCnt : Byte; { число таблиц FAT }RootSiz : Word; { макс.число 32-байтовых элементов корневого оглавления }TotSecs : Word; { общее число секторов на носителе (раздел DOS) }Media : Byte; { дескриптор носителя (то же, что 1-й байт FAT) }FatSize : Word; { число секторов в одной FAT }TrkSecs : Word; { секторов на дорожку (цилиндр) }HeadCnt : Word; { число головок чтения/записи (поверхностей) }HidnSec : Word; { спрятанных секторов (исп. в схемах разделения) }Ostatok : Array[1..486] of Char; { Остаток, что-бы сюда можно было читать сектор }End;type TFile = RecordName : TStr8; { Имя файла }Ext : TStr3; { Расширение }Attr : Byte; { Атрибут файла }Reserv : TStr10; { Резерв }Time : Word; { время создания/модификации в формате filetime }Date : Word; { дата создания/модификации в формате filetime }ClstrNo : Word; { номер начального кластера данных (связь с FAT ) }Size : LongInt; { размер файла в байтах }End;type TLongSearchRec = RecordName : TStr8; { Имя файла }LongName : String; { Длиное имя файла }Ext : TStr3; { Расширение }Attr : Byte; { Атрибут файла }Reserv : TStr10; { Резерв }Time : Word; { время создания/модификации в формате filetime }Date : Word; { дата создания/модификации в формате filetime }ClstrNo : Word; { номер начального кластера данных (связь с FAT ) }Size : LongInt; { размер файла в байтах }FPosInSec : Integer; { Позиция - в секторе }FSecPos : Integer; { Позиция - сектор }FMask : String; { Маска для поиска }FAttr : Byte; { Путь заданный в FindFirst }End;type TDTA = RecordReserved : Array[1..$15] of Byte; { Зарезервированно для FindNext }Attr : Byte; { Атрибут файла }Time : Word; { время создания/модификации в формате filetime }Date : Word; { дата создания/модификации в формате filetime }Size : LongInt; { размер файла в байтах }Name : Array[1..13] of Char; { Имя файла + . +расширение }End;type TDir=Array[0..15] of TFile;var s0 : TSector0;dir : TDir;Root : Word;I : Word;Search : TLongSearchRec;LongDosError : Integer;SecPerCat : Integer;DTA : TDTA; { Текущая DTA }Function ReadSector(A:Pointer;Disk:Byte;Count:Word;Sector:Word):Boolean;var Error :Word;Offs :Word;BeginOffs:=Word(A);Error:=0;asmmov al,[Disk] {Номер диска 0-A}mov cx,[Count] {Счетчик считываемых символов}mov dx,[Sector] {Начальный сектор}mov bx,[Offs]int 25hpop dxje @nErrormov ax,1mov [Error],ax@nError:end;if Error=0 then ReadSector:=Trueelse ReadSector:=False;End;{ Получаем длиное имя файла #################################################}function GetUniLFN(I:Word):String;var Str,Str0,Str1:String;Flag:Boolean;Len:Integer;BeginFlag:=true;Str0:=#255+#255;Str:='';Str1:='';While flag doBeginStr1:=copy(Dir[I-1].Name,2,7)+Dir[I-1].Ext+copy(Dir[I-1].Reserv,3,8)+char(Dir[I-1].Time)+char(Dir[I-1].Time shr 8)+char(Dir[I-1].Date)+char(Dir[I-1].Date shr 8)+char(Dir[I-1].Size)+char(Dir[I-1].Size shr 8)+char(Dir[I-1].Size shr 16)+char(Dir[I-1].Size shr 24);Len:=pos(Str0,Str1);If Len<>0 then Begin Flag:=False; Str:=Str+copy(Str1,1,Len); Endelse Begindec(i);Str:=Str+Str1;if Dir[I].Attr<>15 then Flag:=False;End;End;GetUniLFN:=Str;End;{ Long Find First ###########################################################}Procedure LongFindFirst(Path:String;Attr:Word;var SR:TLongSearchRec);var I,Sec :Integer;Drive :Word; { Номер диска }NameSeg,NameOfs :Word; { Адрес строки для поиска }ParentDir :String; { Родительский каталог }Sector,InSect,Data :LongInt; { Сектор, и позиция каталоговой записи }DTASeg,DTASize,DTAOfs :Word; { Адрес локальгого DTA }BeginLongDOSError:=0;{Drive:=Path[1];} Drive:=0; { Получаем имя диска }ParentDir:=copy(Path,4,Length(Path)-1); { Выделим родительский каталог }If not ReadSector(@s0,Drive,1,0) then Exit;If Pos('\',ParentDir)=0 thenBegin { Если ищем в корне #################################################}Path:=Path+#0;NameSeg:=Seg(Path);NameOfs:=Ofs(Path)+1;DTAOfs:=Ofs(DTA);DTASeg:=Seg(DTA);DTASize:=Sizeof(DTA);asm { FindFirst - заполняем }push dsmov ax,[NameSeg]mov ds,axmov dx,[NameOfs]mov ah,$4E { Функция DOS - найти первый файл }mov cx,Attr { Аттрибуты файла }int 21hjnc @FilesFmov ax,$FFmov [LongDOSError],axjmp @AsmEnd@FilesF: mov ah,$2f { Получим адрес текущей DTA }int 21hmov ax,es { И скопируем ее в нашу локальную DTA }mov ds,axmov si,bxmov di,[DTAOfs]mov es,[DTASeg]mov cx,[DTASize]cldrep movsb@AsmEnd: pop dsend; { В DTA.Reserved[14] можно узнать номер найденой записи в каталоге}If LongDOSError<>0 then Exit;Sector:=trunc((DTA.Reserved[14]+256*DTA.Reserved[15])/(s0.SectSize shr 5)); { Номер сектора,где находится каталог с именем }InSect:=(DTA.Reserved[14]+256*DTA.Reserved[15])-Sector*(s0.SectSize shr 5); { Номер записи в секторе }Root:=s0.ResSecs+s0.FatSize*s0.FatCnt; { Вычисляем номер сектора с корнем }If not ReadSector(@dir,0,1,Root+Sector) then Exit; { Читаем сектор }SR.Name:=dir[InSect].Name; { Заполнаяем TLongSearchRec }SR.Ext:=dir[InSect].Ext;SR.Attr:=dir[InSect].Attr;SR.Reserv:=dir[InSect].Reserv;SR.Time:=dir[InSect].Time;SR.Date:=dir[InSect].Date;SR.ClstrNo:=dir[InSect].ClstrNo;SR.Size:=dir[InSect].Size;{ Теперь смотрим длиное имя }If (InSect>0) and (dir[InSect-1].attr=15) then SR.LongName:=UniToDos(GetUniLFN(InSect))else SR.LongName:=dir[InSect].Name+'.'+dir[InSect].Ext;EndelseBegin { Если ищем не в корне ##############################################}Path:=Path+#0;NameSeg:=Seg(Path);NameOfs:=Ofs(Path)+1;DTAOfs:=Ofs(DTA);DTASeg:=Seg(DTA);DTASize:=Sizeof(DTA);asm { FindFirst - заполняем }push dsmov ax,[NameSeg]mov ds,axmov dx,[NameOfs]mov ah,$4E { Функция DOS - найти первый файл }mov cx,Attr { Аттрибуты файла }int 21hjnc @FilesFmov ax,$FFmov [LongDOSError],axjmp @AsmEnd@FilesF: mov ah,$2f { Получим адрес текущей DTA }int 21hmov ax,es { И скопируем ее в нашу локальную DTA }mov ds,axmov si,bxmov di,[DTAOfs]mov es,[DTASeg]mov cx,[DTASize]cldrep movsb@AsmEnd: pop dsend; { В DTA.Reserved[14] можно узнать номер найденой записи в каталоге}If LongDOSError<>0 then Exit;Sector:=trunc((DTA.Reserved[14]+256*DTA.Reserved[15])/(s0.SectSize shr 5));InSect:=(DTA.Reserved[14]+256*DTA.Reserved[15])-Sector*(s0.SectSize shr 5); { Номер записи в секторе }Sector:=Sector+DTA.Reserved[16]+256*DTA.Reserved[17]-3; { Номер сектора,где находится каталог с именем }Root:=s0.ResSecs+s0.FatSize*s0.FatCnt; { Вычисляем номер сектора с корнем }Data:=Root+(s0.RootSiz*32) div s0.SectSize;If not ReadSector(@dir,0,1,Data+Sector) then Exit; { Читаем сектор }SR.Name:=dir[InSect].Name; { Заполнаяем TLongSearchRec }SR.Ext:=dir[InSect].Ext;SR.Attr:=dir[InSect].Attr;SR.Reserv:=dir[InSect].Reserv;SR.Time:=dir[InSect].Time;SR.Date:=dir[InSect].Date;SR.ClstrNo:=dir[InSect].ClstrNo;SR.Size:=dir[InSect].Size;{ Теперь смотрим длиное имя }If (InSect>0) and (dir[InSect-1].attr=15) then SR.LongName:=UniToDos(GetUniLFN(InSect))else SR.LongName:=dir[InSect].Name+'.'+dir[InSect].Ext;End;End;{ Long Find Next ############################################################}Procedure LongFindNext(var SR:TLongSearchRec);varDrive :Word; { Номер диска }Sector,InSect,Data :LongInt; { Сектор, и позиция каталоговой записи }DTASeg,DTASize,DTAOfs :Word; { Адрес локальгого DTA }BeginDrive:=0;LongDOSError:=0;DTAOfs:=Ofs(DTA);DTASeg:=Seg(DTA);DTASize:=Sizeof(DTA);asm { FindFirst - заполняем }push dsmov ah,$2f { Получим адрес текущей DTA }int 21hmov dx,bxmov ah,$4F { Функция DOS - найти первый файл }int 21hjnc @FilesFmov ax,$FFmov [LongDOSError],axjmp @AsmEnd@FilesF: mov ah,$2f { Получим адрес текущей DTA }int 21hmov ax,es { И скопируем ее в нашу локальную DTA }mov ds,axmov si,bxmov di,[DTAOfs]mov es,[DTASeg]mov cx,[DTASize]cldrep movsb@AsmEnd: pop dsend; { В DTA.Reserved[14] можно узнать номер найденой записи в каталоге}If LongDOSError<>0 then Exit;Sector:=trunc((DTA.Reserved[14]+256*DTA.Reserved[15])/(s0.SectSize shr 5));InSect:=(DTA.Reserved[14]+256*DTA.Reserved[15])-Sector*(s0.SectSize shr 5); { Номер записи в секторе }Sector:=Sector+DTA.Reserved[16]+256*DTA.Reserved[17]-3; { Номер сектора,где находится каталог с именем }Root:=s0.ResSecs+s0.FatSize*s0.FatCnt; { Вычисляем номер сектора с корнем }Data:=Root+(s0.RootSiz*32) div s0.SectSize;If not ReadSector(@dir,0,1,Data) then Exit; { Читаем сектор }SR.Name:=dir[InSect].Name; { Заполнаяем TLongSearchRec }SR.Ext:=dir[InSect].Ext;SR.Attr:=dir[InSect].Attr;SR.Reserv:=dir[InSect].Reserv;SR.Time:=dir[InSect].Time;SR.Date:=dir[InSect].Date;SR.ClstrNo:=dir[InSect].ClstrNo;SR.Size:=dir[InSect].Size;{ Теперь смотрим длиное имя }If (InSect>0) and (dir[InSect-1].attr=15) then SR.LongName:=UniToDos(GetUniLFN(InSect))else SR.LongName:=dir[InSect].Name+'.'+dir[InSect].Ext;End;BeginWriteLn;InitUni;LongFindFirst('A:\1\*.*',$FFFF,Search);While LongDosError=0 doBeginWriteLn(Search.Name+' '+Search.Ext+' ',Search.Size:8,' ',Search.FPosInSec:8,' ',Search.FSecPos:8,' ',Search.LongName);LongFindNext(Search);Search.LongName:=copy(Search.LongName,1,30);End;End.
Download this code: lfn.pas