Для обеспечения работы процедур первых двух групп в модуле DOS вводятся специальные типы и константы. Так, для ввода атрибутов файла или их анализа определены константы
CONST
ReadOnly = $01; { только для чтения }
Hidden = $02; { скрытый файл }
SysFile = $04; { системный (непереносимый) }
VolumeID = $08; { метка диска }
Directory = $10; { подкаталог }
Archive = $20; { архивный (для BACKUP) }
AnyFile = $3F; { сумма всех предыдущих }
При использовании их можно складывать. Так, имя файла имеет шестнадцать вариантов композиций атрибутов ReadOnly, Hidden, SysFile, Archive; имя подкаталога может быть скрытым и т.п. Не стоит только суммировать что-либо с константой AnyFile, ибо она уже есть сумма всех предыдущих.
Для процедур FindFirst и FindNext введен тип SearchRec:
TYPE
SearchRec = RECORD
Fill: Array[1..21] of Byte; {системное поле }
Attr: Byte; {поле атрибута }
Time: LongInt; {запись времени }
Size: Longlnt; {размер файла }
Name: String[12]; {имя файла }
END;
Поля переменной этого типа содержат информацию по последнему найденному имени файла или подкаталога. Кроме того, предопределены еще две записи для поддержания работы с файлами. Для всех {357} файлов (типизированных или бестиповых), кроме текстовых, имеется системный тип:
TYPE
FileRec = RECORD
Handle: Word;
Mode: Word;
RecSize: Word;
Private: Array [1..26] of Byte;
UserData: Array [1..16] of Byte;
Name: Array [0..79] of Char;
END;
Поле Handle содержит специальную информацию для файловых функций MS-DOS. Поле Mode типа Word хранит специальное число, характеризующее состояние файла. Для работы с этим полем определены четыре константы (и только их значения могут содержаться в этом поле):
CONST
fmClosed = $D7B0; { файл закрыт }
fmInput = $0781; { текстовый файл открыт для чтения }
fmOutput = $D782; { текстовый файл открыт для записи }
fmInOut - $D7B3; { нетекстовый файл открыт для доступа }
{Все прочие значения говорят, что файл ни с чем не связан. }
Сравнивая значения констант со значением поля Mode, можно определить текущий статус файла.
Следующее поле записи FileRec — RecSize — содержит длину записи файла (она определяется типом компонентов файла). Для бестиповых файлов она устанавливается процедурами Rewrite или Reset, а для текстовых — процедурой SetTextBuf.
Поле Private, состоящее из 26 байт, зарезервировано и не используется, зато поле UserData может содержать практически любую информацию размером до 16 байт. Оно играет роль комментария к файлу. Здесь уместно напомнить, что основное назначение типа FileRec — обслуживание внутренних процессов языка. Из полей можно извлекать информацию, но запись в них может привести к непредсказуемым, а то и фатальным последствиям. (Исключение составляет процесс написания собственных драйверов текстовых файлов, что требует весьма высокой квалификации и знания «внутренностей» MS-DOS.) Поле UserData — единственное безопасное в этом плане.
Последний компонент записи FileRec — поле Name, которое содержит полное имя физического файла на диске. Заметим, что тип {358} имени не соответствует типу строки, и вдобавок к этому имя заканчивается символом #0.
Все сказанное выше относилось к нетекстовым файлам. Для текстовых файлов используется запись с другой структурой:
TYPE
TextBuf = Array[0..127] of Char; { стандартный буфер }
TextRec = RECORD
Handle: Word;
Mode: Word; { состояние файла fmXXX }
BufSize: Word; { размер буфера }
Private: Word;
BufPos: Word;
BufEnd: Word;
BufPtr: ^TextBuf; { адрес буфера в памяти }
OpenFunc: Pointer; { адреса драйверов: }
InOutFunc: Pointer;
FlushFunc: Pointer;
CloseFunc: Pointer;
UserData: Array [1..16] of Byte;
Name: Array [0..79] of Char;
Buffer: TextBuf;
END;
Первые поля имеют тот же смысл, что и для нетекстовых файлов. Поле Mode может содержать те же значения констант. Дополнительные поля отведены для работы с текстовым буфером, а группа полей типа Pointer содержит адреса функций — драйверов текстовых файлов. Если в них не записаны адреса своих текстовых драйверов, то принимаются системные.
Кроме случаев создания собственных драйверов, следует избегать внесения изменений в поля записи (кроме поля UserData). Это требование остается и для текстовых файлов.
Если кого-либо заинтересовала возможность использования системной информации о файлах, то обязательно появится вопрос: а как к ней добраться? Ни одна функция или процедура языка не имеет аргумента типа FileRec или TextRec. Создание переменной таких типов будет пустой тратой памяти. Оказывается, что это и не нужно. Для работы с файлами необходимо объявлять их переменные, например:
VAR
tf: Text; { текстовый файл }
fr: File of Real; { файл вещественных чисел }
f: File; { бестиповый файл }
{359}
Предопределено, что структура типа File и File of... соответствует типу FileRec (размер 128 байт), а типа Text — типу TextRec (paзмер 256 байт). Используя операцию преобразования типов, легко опросить любое поле системного типа. Примеры этого:
if TextRec(tf).Mode = fmOutput then...;
if FileRec(fr).Mode <> fmlnOut then...;
ByteVAR:= FileRec(f).UserData[4];
и т.п. Пример извлечения имени файла из записи:
StringVAR:= ' '; i:= 0; { имя файла }
repeat
StringVAR:= StringVAR + TextRec(tf).Name[i];
Inc(i)
until StringVAR[i]=#0;
...
Работая таким образом с файловыми переменными, следует всегда помнить, что файловая переменная f перед анализом обязательно должна «пройти» через процедуры Assign (f, имя физического файла) и Reset или Rewrite. В противном случае поля типов будут содержать в буквальном смысле что угодно, кроме полезной информации.
Возможное применение описанных записей — анализ состояния конкретных файлов через поле Mode, добавление комментариев в поле UserData. Это достаточно полезно и относительно несложно. Но основное их назначение — подключение собственных файловых драйверов. Этот вопрос слишком сложен и в этой книге не рассматривается.
Зато все вышеизложенное можно с пользой применять при отладке программ в среде Турбо Паскаль. Если, например, непонятно, что творится с логическим текстовым файлом MyFile, то можно поместить его в окно отладчика Watch в преобразованном виде:
TextRec(MyFile), R |
И при трассировке поля записи будут заполняться внутренними параметрами. Подобный фокус можно провести и с окном опроса Evaluate.
Для третьей группы (процедуры FSplit и функции FExpand) вводятся специальные типы для ведения имен файлов и подкаталогов: {360}
TYPE
ComStr = String [127]; { для командной строки }
PathStr = String [79]; { для полного имени файла }
DirStr = String [67]; { для маршрута на диске }
NameStr = String [8]; { только имя файла }
ExtStr = String [4]; { точка и расширение }
Для работы с процедурами поисков файлов и анализа имен необходимо вводить переменные именно таких типов. Размеры строк в типах соответствуют ограничениям MS-DOS на длину имен и маршрутов.
Для работы с файлами может также пригодиться системная переменная модуля DOS — DosError.
Переменная DosError
Предопределенная переменная DosError типа Integer содержит номер ошибки, возвращаемой MS-DOS, при неправильной операции. Эта переменная равна нулю до тех пор, пока не произойдет сбой во время работы программы при обращении к функциям DOS. Возможные значения DosError:
0 — нет ошибки;
2 — не найден файл (невозможно связать логический файл с физическим устройством);
3 — путь (маршрут) не найден (неверная адресация файла на диске);
4 — слишком много открытых файлов (больше, чем указано в директиве FILES =... файла CONFIG.SYS);
5 — доступ закрыт (операция заблокирована средствами DOS, или нарушаются правила работы с именами файлов или каталогов);
6 — неверное ведение файла (редкая ошибка, возникающая при нарушении информации в полях файловых переменных или в системных областях MS-DOS);
8 — не хватает памяти;
10 — несовместная операционная среда;
11 — нераспознаваемая разметка (формат) диска;
12 — неверный код доступа к диску (ошибка в поле Mode файловой записи);
18 — искомые файлы исчерпаны (возникает при поиске файлов по шаблонам).
Обычно переменная DosError используется в сопряжении с процедурами FindFirst и FindNext. {361}