if (dir[0] == '\0') /* current directory */
dir = ".";
d = 3; /* minimum distance */
if ((fd = open(dir, 0)) == -1)
return d;
while (read(fd,(char *)&nbuf, sizeof(struct direct)) > 0)
if (nbuf.ino) {
nd = spdist(nbuf.name, guess);
if (nd <= d && nd != 3) {
strcpy(best, nbuf.name);
d = nd;
if (d == 0) /* exact match */
break;
}
}
close(fd);
return d;
Если имя каталога, данное
mindist
, пустое, отыскивается '.'
. Функция mindist
читает одну строку каталога за один раз. Отметим, что буфер для read
представляет собой структуру, а не массив символов. Мы используем sizeof
, чтобы вычислить число байтов и привести адрес к символьному указателю.Если строка каталога в данный момент не используется (поскольку файл удален), то поле индекса в ней равно нулю и она пропускается. Проверка расстояния осуществляется как
if (nd <= d...)
а не как
if (nd < d...)
поэтому любой одиночный символ дает лучшее совпадение, чем имя
'.'
, которое всегда является первой строкой в каталоге./* spdist: return distance between two names */ /*
* very rough spelling metric:
* 0 if the strings are identical
* 1 if two chars are transposed
* 2 if one char wrong, added or deleted
* 3 otherwise
*/
#define EQ(s,t) (strcmp(s,t) == 0)
spdist(s, t)
char *s, *t;
{
while (*s++ == *t)
if (*t++ == '\0')
return 0; /* exact match */
if (*--s) {
if (*t) {
if (s[1] && t[1] && *s == t[1] && *t == s[1] && EQ(s+2, t+2))
return 1; /* transposition */
if (EQ(s+1, t+1))
return 2; /* 1 char mismatch */
}
if (EQ(s+1, t))
return 2; /* extra character */
}
if (*t && EQ(s, t+1))
return 2; /* missing character */
return 3;
}
Поскольку у нас есть
spname
, несложно вставить функции по коррекции написания в p
:/* p: print input in chunks (version 4) */
#include
#define PAGESIZE 22
char *progname; /* program name for error message */
main(argc, argv)
int argc;
char *argv[];
{
FILE *fp, *efopen();
int i, pagesize = PAGESIZE;
char *p, *getenv(), buf[BUFSIZ];
progname = argv[0];
if ((p=getenv("PAGESIZE")) != NULL)
pagesize = atoi(p);
if (argc > 1 && argv[1][0] == '-') {
pagesize = atoi(&argv[1][1]);
argc--;
argv++;
}
if (argc == 1)
print(stdin, pagesize);
else
for (i = 1; i < argc; i++)
switch (spname(argv[i], buf)) {
case -1: /* no match possible */
fp = efopen(argv[i], "r");
break;
case 1: /* corrected */
fprintf(stderr, "\"%s\"? ", buf);
if (ttyin() == 'n')
break;
argv[i] = buf; /* fall through... */
case 0: /* exact match */
fp = efopen(argv[i], "r");
print(fp, pagesize);
fclose(fp);
}
exit(0);
}
Функции по коррекции написания не следует слепо применять к каждой программе, которая использует имена файлов. Они хорошо сочетаются с
p
, так как p
— диалоговая программа, но подходят и для недиалоговых программ.Упражнение 7.5Насколько вы можете улучшить эвристику для выявления наилучшего совпадения в
spname
? Например, неразумно рассматривать регулярный файл так, как если бы он был каталогом; текущая версия это допускает.Упражнение: 7.6Имя
tx
совпадает с каким-либо именем tc
, которое оказывается последним в каталоге для любого одиночного символа с
. Можете ли вы придумать лучшую меру расстояния? Реализуйте ее и посмотрите, насколько хорошо эта конструкция работает с реальными пользователями.Упражнение 7.7Работает ли
p
ощутимо быстрее, если чтение каталога выполняется большими порциями?Упражнение 7.8Модифицируйте
spname
, чтобы возвращать имя, которое является префиксом желаемого имени, если нельзя найти более точного совпадения. Как следует разрешить ситуацию, если несколько имен совпадают с префиксом?Упражнение 7.9Какую пользу могли бы извлечь другие программы из
spname
? Сконструируйте отдельную программу, которая корректировала бы другие аргументы прежде, чем передать их другой программе, как в$ fix prog filenames...
Можете написать версию
cd
, которая использует spname
? Как бы вы ее встроили?7.3 Файловая система: индексные дескрипторы
Теперь мы обсудим системные вызовы применительно к файловой системе, в частности к такой информации о файлах, как размеры, даты изменений, права доступа и т.д. Эти системные вызовы позволяют получить полностью всю информацию, о которой упоминалось во второй главе.
Для начала разберемся в самом индексном дескрипторе. Часть индексного дескриптора описывается структурой stat, определенной в
:struct stat /* структура, возвращаемая stat */
{
dev_t st_dev; /* устройство, содержащее файл */
ino_t st_ino; /* индекс */
short st_mod; /* биты режима */
short st_nlink; /* число связей файла */
short st_uid; /* пользовательский идентификатор
владельца */
short st_gid; /* идентификатор группы владельцев */
dev_t st_rdev; /* для специальных файлов */
off_t st_size; /* размер файла в символах */
time_t st_atime; /* время последнего чтения файла */
time_t st_mtime; /* время последней записи
или создания файла */