UNIX — страница 70 из 115

 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; /* время последней записи

                     или создания файла */