Язык программирования C

       

Пример - распечатка справочников


Иногда требуется другой вид взаимодействия с системой файлов - определение информации о файле, а не того, что в нем содержится. Примером может служить команда ls ("список справочника") системы UNIX. По этой команде распечатываются имена файлов из справочника и, необязательно, другая информация, такая как размеры, разрешения и т.д.

Поскольку, по крайней мере, на системе UNIX справочник является просто файлом, то в такой команде, как ls нет ничего особенного; она читает файл и выделяет нужные части из находящейся там информации. Однако формат информации определяется системой, так что ls должна знать, в каком виде все представляется в системе.

Мы это частично проиллюстрируем при написании программы fsize. Программа fsize представляет собой специальную форму ls, которая печатает размеры всех файлов, указанных в списке ее аргументов. Если один из файлов является справочником, то для обработки этого справочника программа fsize обращается сама к себе рекурсивно. если же аргументы вообще отсутствуют, то обрабатывается текущий справочник.

Для начала дадим краткий обзор структуры системы файлов. Справочник - это файл, который содержит список имен файлов и некоторое указание о том, где они размещаются. Фактически это указание является индексом для другой таблицы, которую называют "I - узловой таблицей". Для файла I-узел - это то, где содержится вся информация о файле, за исключением его имени. Запись в справочнике состоит только из двух элементов: номера I-узла и имени файла. Точная спецификация поступает при включении файла sys/dir.h, который содержит

#define dirsiz 14 /*max length of file name*/ struct direct /*structure of directory entry*/ { ino_t& _ino; /*inode number*/ char & _name[dirsiz]; /*file name*/ };

"Тип" ino_t - это определяемый посредством typedef тип, который описывает индекс I-узловой таблицы. На PDP-11 UNIX этим типом оказывается unsigned, но это не тот сорт информации, который помещают внутрь программы: на разных системах этот тип может быть различным. Поэтому и следует использовать typedef. Полный набор "системных" типов находится в файле sys/tupes.h.

функция stat берет имя файла и возвращает всю содержащуюся в I-ом узле информацию об этом файле (или -1, если имеется ошибка). Таким образом, в результате


struct stat stbuf; char *name; stat(name,&stbuf);

структура stbuf наполняется информацией из I-го узла о файле с именем name. структура, описывающая возвращаемую функцией stat информацию, находится в файле sys/stat.h и выглядит следующим образом:

struct stat /*structure returned by stat*/ { dev_t st_dev; /* device of inode */ ino_t st_ino; /* inode number */ short st_mode /* mode bits */ short st_nlink; / *number of links to file */ short st_uid; /* owner's user id */ short st_gid; /* owner's group id */ dev_t st_rdev; /* for special files */ off_t st_size; /* file size in characters */ time_t st_atime; /* time last accessed */ time_t st_mtime; /* time last modified */ time_t st_ctime; /* time originally created */ }

Большая часть этой информации объясняется в комментариях. Элемент st.mode содержит набор флагов, описывающих файл; для удобства определения флагов также находятся в файле sys/stat.h.

#define s_ifmt 0160000 /* type of file */ #define s_ifdir 0040000 /* directory */ #define s_ifchr 0020000 /* character special */ #define s_ifblk 0060000 /* block special */ #define s_ifreg 0100000 /* regular */ #define s_isuid 04000 /* set user id on execution */ #define s_isgid 02000 /* set group id on execution */ #define s_isvtx 01000 /*save swapped text after use*/ #define s_iread 0400 /* read permission */ #define s_iwrite 0200 /* write permission */ #define s_iexec 0100 /* execute permission */

Теперь мы в состоянии написать программу fsize. Если полученный от функции stat режим указывает, что файл не является справочником, то его размер уже под рукой и может быть напечатан непосредственно. Если же он оказывается справочником, то мы должны обрабатывать этот справочник отдельно для каждого файла; так как справочник может в свою очередь содержать подсправочники, этот процесс обработки является рекурсивным.

Как обычно, ведущая программа главным образом имеет дело с командной строкой аргументов; она передает каждый аргумент функции fsize в большой буфер.

#include <stdio.h> #include <sys/types.h>> /*typedefs*/ #include <sys/dir.h>> /*directory entry structure*/ #include <sys/stat.h>> /*structure returned by stat*/ #define bufsize 256 main(argc,argv) /*fsize:print file sizes*/ char *argv[]; { char buf[bufsize]; if(argc==1) { /*default:current directory*/ atrcpy(buf,"."); fsize(buf); } else while(--argc>0) { strcpy(buf,*++argv); fsize(buf); } }



Функция fsize печатает размер файла. Если однако файл оказывается справочником, то fsize сначала вызывает функцию directory для обработки всех указанных в нем файлов. Обратите внимание на использование имен флагов s_ifmt и _ifdir из файла stat.h.

fsize(name) /*print size for name*/ char *name; { struct stat stbuf; if(stat(name,&stbuf)== -1) { fprintf(stderr,"fsize:can't find %s\n",name); return; } if((stbuf.st_mode & s_ifmt)==s_ifdir) directory(name); printf("%8ld %s\n",stbuf.st_size,name); }

Функция directory является самой сложной. Однако значительная ее часть связана с созданием для обрабатываемого в данный момент файла его полного имени, по которому можно восстановить путь в дереве.

directory(name) /*fsize for all files in name*/ char *name; { struct direct dirbuf; char *nbp, *nep; int i, fd; nbp=name+strlen(name); *nbp++='/'; /*add slash to directory name*/ if(nbp+dirsiz+2>=name+bufsize) /*name too long*/ return; if((fd=open(name,0))== -1) return; while(read(fd,(char *)&dirbuf,sizeof(dirbuf))>0) \( if(dirbuf.d_ino==0) /*slot not in use*/ continue; if(strcmp (dirbuf.d_name,".")==0 || strcmp(dirbuf.d_name,"..")==0 continue; /*skip self and parent*/ for (i=0,nep=nbp;i<dirsiz;i++) *nep++=dirbuf.d_name[i]; *nep++='\0'; fsize(name); } close(fd); *--nbp='\0'; /*restore name*/ }

Если некоторая дыра в справочнике в настоящее время не используется (потому что файл был удален), то в соответствующее I-узловое число равно нулю, и эта позиция пропускается. Каждый справочник также содержит запись в самом себе, называемую ".", и о своем родителе, ".."; они, очевидно, также должны быть пропущены, а то программа будет работать весьма и весьма долго.

Хотя программа fsize довольно специализированна, она все же демонстрирует пару важных идей. во-первых, многие программы не являются "системными программами"; они только используют информацию, форма или содержание которой определяется операционной системой. Во-вторых, для таких программ существенно, что представление этой информации входит только в стандартные "заголовочные файлы", такие как stat.h и dir.h, и что программы включают эти файлы, а не помещают фактические описания внутрь самих программ.


Содержание раздела