Файлова система UNIX: каталоги

Тепер подивимося, як переміщатися по ієрархії каталогів Для цього не потрібні нові системні виклики, просто використовуємо старі в новому контексті Пояснимо на прикладі функції spname, яка спробує впоратися з неправильним імям файлу Функція

n = spname (імя, новое5імя)

шукає файл з імям, «досить схожим» на задане параметром імя Якщо таке знайдено, воно копіюється в параметр новое5імя Значення, що повертається, дорівнює -1, якщо нічого схожого не знайдено, 0 – якщо знайдено точну відповідність, і 1 – якщо знадобилося виправлення

Функція spname являє собою зручне доповнення до програми p Якщо користувач хоче надрукувати файл, але помилився в написанні його імені, програма p може попросити його уточнити, що малося на увазі:

$ p / urs / srx / ccmd / p / spnamс Абсолютно неправильне імя

“/ Usr / src / cmd / p / spnameс? уЗапропоноване виправлення прийнято

/ * Spname: повернути правильно написане імя файлу * /

Напишемо функцію так, щоб вона намагалася виправити в кожному елементі імені файлу такі помилки: одна пропущена або зайва літера, одна буква неправильна, пара букв перепутана місцями – всі ці варіанти є в прикладі Така програма буде просто знахідкою для неуважною друкарки

Перед тим як почати програмувати, зробимо короткий екскурс в структуру файлової системи Каталог – це файл, що містить пере-

чень імен файлів із зазначенням їх розташування Під «розташуванням» тут розуміється індекс файлу в іншій таблиці, званої таб5 ліцей індексних дескрипторів Запис у цій таблиці, звана індексним дескриптором (inode) файлу, – це те місце, де зберігається вся інформація про нього, за винятком імені Таким чином, запис в каталозі складається з двох елементів – Номери індексного дескриптора та імені файлу Точна специфікація знаходиться у файлі sys / dirh:

$ cat  /usr/include/sys/dirh

# Define DIRSIZ 14 / * максимальна довжина імені файлу * /

struct direct / * структура запису в каталозі * /

{

ino_t d_ino / * Номер індексного дескриптора * / char d_name [DIRSIZ] / * Імя файлу * /

}

$

«Тип» даних ino_t, визначений за допомогою typedef, описує запис у таблиці індексних дескрипторів У системах PDP-11 і VAX це тип unsigned short, але в інших системах він, скоріш за все, буде іншим, не варто покладатися на це в програмах, тому й використаний typedef Повний набір «системних» типів знаходиться у файлі sys / typesh, який повинен бути включений до файлу sys / dirh

Алгоритм роботи spname досить простий Припустимо, треба обробити імя файлу / d1/d2/f Основна ідея полягає в тому, щоб відкинути перший елемент (/) І шукати в кореневому каталозі імя, максимально збігалася з наступним елементом (d1), потім у знайденому каталозі шукати небудь подібне d2 і так далі, поки не будуть знайдені відповідності для всіх елементів Якщо в черговому каталозі не вдалося знайти відповідне імя, пошук припиняється

Вся робота розділена між трьома функціями: власне spname відокремлює елементи один від одного і складає з них «найбільш ймовірне» імя файлу Функція mindist викликається з spname і виконує в заданому каталозі пошук файлу з імям, максимально схожим на заданий, звертаючись при цьому до функції spdist, що обчислює «відстань» між двома іменами

/ * Spname: повертає правильно написане імя файлу * /

/*

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp spname(oldname,  newname)  char  *oldname,  *newname

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp returns –1 if no reasonable match to  oldname,

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp 0 if exact  match,

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp 1 if corrected

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp stores corrected name  in  newname

*/

#include &ltsys/typesh&gt

#include &ltsys/dirh&gt

spname(oldname, newname)

char  *oldname,  *newname

{

char  *p,  guess[DIRSIZ+1],  best[DIRSIZ+1] char  *new  =  newname, *old  =  oldname

for  (;) {

while (* old == /) / * пропустити Слеш * /

*new++  = *old++

*new  = \0;

if (* old == \ 0) / * правильно чи виправлено * / return strcmp (oldname, newname) = 0

p = guess / * Скопіювати наступний компонент в guess * / for ( * old = / && * Old = \ 0; old + +)

if (p  &lt guess+DIRSIZ)

*p++ = *old

*p = \0;

if (mindist (newname, guess, best)> = 3) return -1 / * Невдало * /

for (p = best * new = * p + +) / * додати в кінець * / new + + / * Нового імені * /

}

}

mindist (dir, guess, best) / * перегляд dir в пошуку guess * / char * dir, * guess, * best

{

/ * Встановлює best, повертає відстань 0 . 3 * / int d, nd, fd

struct {

ino_t ino

char name [DIRSIZ +1] / * На 1 більше, ніж у dirh * /

} nbuf

nbufname [DIRSIZ] = \ 0; / * +1 Для заключного \ 0 * / if (dir [0] == \ 0) / * поточний каталог * /

dir = &quot."

d = 3 / * Мінімальна відстань * / if ((fd = open (dir, 0)) == -1)

return d

while  (read(fd,(char  *)  &ampnbuf,sizeof(struct  direct))  &gt 0) if  (nbufino) {

nd =  spdist(nbufname,  guess) if  (nd  &lt=  d  &amp&amp  nd  =  3)  {

strcpy(best,  nbufname) d  =  nd

if (d == 0) / * точний збіг * / break

}

}

close(fd) return d

}

Якщо імя каталогу, передане в функцію mindist, порожньо, то пошук виконується в поточному каталозі (), За один прохід обробляється один каталог Зверніть увагу, що в якості буфера для читання виступає структура, а не символьний масив Розмір визначається за допомогою sizeof, а потім адресу перетвориться в покажчик на тип char

Коли запис в каталозі не використовується (якщо файл був видалений), то відповідна запис, що містить нульове значення індексного дескриптора, пропускається Відстань проверятся виразом

if (nd  &lt= d ..)

а не

if (nd  &lt d ..)

так що будь-який символ вважається більш підходящим, ніж точка «», яка завжди є першим елементом каталогу

/ * Spdist: повертає відстань між двома іменами * /

/*

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp дуже грубий показник правильності написання:

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp 0, якщо два рядки ідентичні

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp 1, якщо два символи переставлені місцями

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp 2, якщо один символ доданий, видалений або не збігається

*&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp 3 – інакше

*/

#define  EQ(s,t)  (strcmp(s,t)  == 0) spdist(s,  t)

char  *s,  *t

{

while  (*s++  == *t)

if (*t++  == \0)

return 0 / * Точний збіг * / if (* – s) {

if (*t) {

if (s[1] &amp&amp   t[1] &amp&amp   *s  == t[1]

&& * T == s [1] && EQ (s +2, t +2)) return 1 / * Перестановка * /

if (EQ(s+1,  t+1))

return 2 / * 1 неспівпадаючий символ * /

}

if (EQ(s+1, t))

return 2 / * Зайвий символ * /

}

if (*t &amp&amp   EQ(s,  t+1))

return 2 / * Бракуючий символ * / return 3

}

Тепер, коли у нас є функція spname, дуже просто додати перевірку написання до команди p:

/ * P: друкує введення порціями (версія 4) * /

#include &ltstdioh&gt

#define PAGESIZE         22

char * progname / * Імя програми для повідомлення про помилку * /

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(&quotPAGESIZE&quot))  =  NULL) pagesize  =  atoi(p)

if  (argc &gt  1  &amp&amp  argv[1][0]  == –)  { pagesize  =  atoi(&ampargv[1][1]) argc––

argv++

}

if  (argc == 1)

print(stdin,  pagesize) else

for  (i = 1  i &lt argc i++)

switch  (spname(argv[i],  buf))  {

case -1: / * збіг неможливо * / fp = efopen (argv [i], r)

break

case 1: / * виправлено * / fprintf (stderr, \% s \ ?”, buf) if (ttyin () == n)

break argv[i] =  buf

/ * Неуадчно .. * /

case 0: / * точний збіг * / fp = efopen (argv [i], r) print (fp, pagesize) fclose (fp)

} exit(0)

}

Автоматичне виправлення імен файлів треба використовувати з певною обережністю Воно добре працює в інтерактивних програмах, таких як p, але не підходить для програм, які можуть працювати без спілкування з користувачем

Вправа 75 Наскільки можна було б удосконалити евріс тичний алгоритм найкращого збігу в spname Наприклад, немає сенсу обробляти імя файлу як каталог, а в даній версії це можливо ~

Вправа 76 Імя tx завжди буде вважатися відповідним, якщо ка талог закінчується на tc, Незалежно від значення c Чи можна поліпшити визначення відстані Напишіть програму і подивіться, як вона сподобається користувачам ~

Вправа 77Функція mindist читає каталог поелементно Мож але чи істотно прискорити програму p, виконуючи читання великими блоками ~

Вправа 78 Змініть функцію spname так, щоб вона повертала префікс передбачуваного імені, якщо не вдалося знайти достатньо близьке відповідність Що робити, якщо кілька імен відповідають цьому префіксу ~

Вправа 79У яких ще програмах можна використовувати spna-me Напишіть автономну програму, що виправляє отримані аргументи перед тим, як передати їх в іншу програму, наприклад таку, як

$ fix prog імена5файлов..

Чи можна написати версію cd з використанням spname Як її проинсталлировать ~

Джерело: Керниган Б, Пайк Р, UNIX Програмне оточення – Пер з англ – СПб: Символ-Плюс, 2003 – 416 с, Мул

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*