📄 chapter8.html
字号:
int fseek(FILE *fp, long offset, int origin)</pre>is identical to <tt>lseek</tt> except that <tt>fp</tt> is a file pointer instead ofa file descriptor and return value is an <tt>int</tt> status, not a position.Write <tt>fseek</tt>. Make sure that your <tt>fseek</tt> coordinates properlywith the buffering done for the other functions of the library.<h2><a name="s8.6">8.6 Example - Listing Directories</a></h2>A different kind of file system interaction is sometimes called for -determining information <em>about</em> a file, not what it contains. Adirectory-listing program such as the UNIX command <tt>ls</tt> is an example -it prints the names of files in a directory, and, optionally, otherinformation, such as sizes, permissions, and so on. The MS-DOS <tt>dir</tt>command is analogous.<p>Since a UNIX directory is just a file, <tt>ls</tt> need only read it to retrievethe filenames. But is is necessary to use a system call to access otherinformation about a file, such as its size. On other systems, a system callmay be needed even to access filenames; this is the case on MS-DOS forinstance. What we want is provide access to the information in a relativelysystem-independent way, even though the implementation may be highlysystem-dependent.<p>We will illustrate some of this by writing a program called <tt>fsize</tt>.<tt>fsize</tt> is a special form of <tt>ls</tt> that prints the sizes of all filesnamed in its commandline argument list. If one of the files is a directory,<tt>fsize</tt> applies itself recursively to that directory. If there are noarguments at all, it processes the current directory.<p>Let us begin with a short review of UNIX file system structure. A<em>directory</em> is a file that contains a list of filenames and someindication of where they are located. The ``location'' is an index intoanother table called the ``inode list.'' The <em>inode</em> for a file iswhere all information about the file except its name is kept. A directoryentry generally consists of only two items, the filename and an inode number.<p>Regrettably, the format and precise contents of a directory are not the sameon all versions of the system. So we will divide the task into two pieces totry to isolate the non-portable parts. The outer level defines a structurecalled a <tt>Dirent</tt> and three routines <tt>opendir</tt>,<tt>readdir</tt>, and <tt>closedir</tt> to provide system-independent accessto the name and inode number in a directory entry. We will write<tt>fsize</tt> with this interface. Then we will show how to implement theseon systems that use the same directory structure as Version 7 and System VUNIX; variants are left as exercises.<p>The <tt>Dirent</tt> structure contains the inode number and the name. The maximumlength of a filename component is <tt>NAME_MAX</tt>, which is a system-dependentvalue. <tt>opendir</tt> returns a pointer to a structure called <tt>DIR</tt>,analogous to <tt>FILE</tt>, which is used by <tt>readdir</tt> and <tt>closedir</tt>.This information is collected into a file called <tt>dirent.h</tt>.<pre> #define NAME_MAX 14 /* longest filename component; */ /* system-dependent */ typedef struct { /* portable directory entry */ long ino; /* inode number */ char name[NAME_MAX+1]; /* name + '\0' terminator */ } Dirent; typedef struct { /* minimal DIR: no buffering, etc. */ int fd; /* file descriptor for the directory */ Dirent d; /* the directory entry */ } DIR; DIR *opendir(char *dirname); Dirent *readdir(DIR *dfd); void closedir(DIR *dfd);</pre>The system call <tt>stat</tt> takes a filename and returns all of theinformation in the inode for that file, or <tt>-1</tt> if there is an error.That is,<pre> char *name; struct stat stbuf; int stat(char *, struct stat *); stat(name, &stbuf);</pre>fills the structure <tt>stbuf</tt> with the inode information for the filename. The structure describing the value returned by <tt>stat</tt> is in<tt><sys/stat.h></tt>, and typically looks like this:<pre> struct stat /* inode information 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; /* owners user id */ short st_gid; /* owners 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 */ };</pre>Most of these values are explained by the comment fields. The types like<tt>dev_t</tt> and <tt>ino_t</tt> are defined in <tt><sys/types.h></tt>, whichmust be included too.<p>The <tt>st_mode</tt> entry contains a set of flags describing the file. Theflag definitions are also included in <tt><sys/types.h></tt>; we need only thepart that deals with file type:<pre> #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 0010000 /* regular */ /* ... */</pre>Now we are ready to write the program <tt>fsize</tt>. If the mode obtained from<tt>stat</tt> indicates that a file is not a directory, then the size is at handand can be printed directly. If the name is a directory, however, then we haveto process that directory one file at a time; it may in turn containsub-directories, so the process is recursive.<p>The main routine deals with command-line arguments; it hands each argument tothe function <tt>fsize</tt>.<pre> #include <stdio.h> #include <string.h> #include "syscalls.h" #include <fcntl.h> /* flags for read and write */ #include <sys/types.h> /* typedefs */ #include <sys/stat.h> /* structure returned by stat */ #include "dirent.h" void fsize(char *) /* print file name */ main(int argc, char **argv) { if (argc == 1) /* default: current directory */ fsize("."); else while (--argc > 0) fsize(*++argv); return 0; }</pre>The function <tt>fsize</tt> prints the size of the file. If the file is adirectory, however, <tt>fsize</tt> first calls <tt>dirwalk</tt> to handle allthe files in it. Note how the flag names <tt>S_IFMT</tt> and <tt>S_IFDIR</tt>are used to decide if the file is a directory. Parenthesization matters,because the precedence of <tt>&</tt> is lower than that of <tt>==</tt>.<pre> int stat(char *, struct stat *); void dirwalk(char *, void (*fcn)(char *)); /* fsize: print the name of file "name" */ void fsize(char *name) { struct stat stbuf; if (stat(name, &stbuf) == -1) { fprintf(stderr, "fsize: can't access %s\n", name); return; } if ((stbuf.st_mode & S_IFMT) == S_IFDIR) dirwalk(name, fsize); printf("%8ld %s\n", stbuf.st_size, name); }</pre>The function <tt>dirwalk</tt> is a general routine that applies a function toeach file in a directory. It opens the directory, loops through the files init, calling the function on each, then closes the directory and returns.Since <tt>fsize</tt> calls <tt>dirwalk</tt> on each directory, the two functionscall each other recursively.<pre> #define MAX_PATH 1024 /* dirwalk: apply fcn to all files in dir */ void dirwalk(char *dir, void (*fcn)(char *)) { char name[MAX_PATH]; Dirent *dp; DIR *dfd; if ((dfd = opendir(dir)) == NULL) { fprintf(stderr, "dirwalk: can't open %s\n", dir); return; } while ((dp = readdir(dfd)) != NULL) { if (strcmp(dp->name, ".") == 0 || strcmp(dp->name, "..")) continue; /* skip self and parent */ if (strlen(dir)+strlen(dp->name)+2 > sizeof(name)) fprintf(stderr, "dirwalk: name %s %s too long\n", dir, dp->name); else { sprintf(name, "%s/%s", dir, dp->name); (*fcn)(name); } } closedir(dfd); }</pre>Each call to <tt>readdir</tt> returns a pointer to information for the next file,or <tt>NULL</tt> when there are no files left. Each directory always containsentries for itself, called <tt>"."</tt>, and its parent, <tt>".."</tt>; these mustbe skipped, or the program will loop forever.<p>Down to this last level, the code is independent of how directories areformatted. The next step is to present minimal versions of <tt>opendir</tt>,<tt>readdir</tt>, and <tt>closedir</tt> for a specific system. The following routinesare for Version 7 and System V UNIX systems; they use the directory informationin the header <tt><sys/dir.h></tt>, which looks like this:<pre> #ifndef DIRSIZ #define DIRSIZ 14 #endif struct direct { /* directory entry */ ino_t d_ino; /* inode number */ char d_name[DIRSIZ]; /* long name does not have '\0' */ };</pre>Some versions of the system permit much longer names and have a morecomplicated directory structure.<p>The type <tt>ino_t</tt> is a <tt>typedef</tt> that describes the index into the inodelist. It happens to be <tt>unsigned short</tt> on the systems we use regularly,but this is not the sort of information to embed in a program; it might bedifferent on a different system, so the <tt>typedef</tt> is better. A completeset of ``system'' types is found in <tt><sys/types.h></tt>.<p><tt>opendir</tt> opens the directory, verifies that the file is a directory(this time by the system call <tt>fstat</tt>, which is like <tt>stat</tt> exceptthat it applies to a file descriptor), allocates a directory structure, andrecords the information:<pre> int fstat(int fd, struct stat *); /* opendir: open a directory for readdir calls */ DIR *opendir(char *dirname) { int fd; struct stat stbuf; DIR *dp; if ((fd = open(dirname, O_RDONLY, 0)) == -1 || fstat(fd, &stbuf) == -1 || (stbuf.st_mode & S_IFMT) != S_IFDIR || (dp = (DIR *) malloc(sizeof(DIR))) == NULL) return NULL; dp->fd = fd; return dp; }</pre><tt>closedir</tt> closes the directory file and frees the space:<pre> /* closedir: close directory opened by opendir */ void closedir(DIR *dp) { if (dp) { close(dp->fd); free(dp); } }</pre>Finally, <tt>readdir</tt> uses <tt>read</tt> to read each directory entry. If adirectory slot is not currently in use (because a file has been removed), theinode number is zero, and this position is skipped. Otherwise, the inode
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -