当前位置:网站首页>The C programming language (version 2) notes / 8 UNIX system interfaces / 8.6 instances (directory list)

The C programming language (version 2) notes / 8 UNIX system interfaces / 8.6 instances (directory list)

2022-06-12 16:28:00 M rookie M

Catalog 、 reference


8.6 example ( Directory listing )

We often need to perform another operation on the file system , To get information about the file , Instead of reading the contents of the file
The directory listing program is an example of this , such as UNIX command ls, It prints the file name in a directory and some other optional information , Such as file length 、 Access rights, etc
MS-DOS In the operating system dir The command has a similar function

because UNIX A directory in is a file , therefore ,ls Just read this file to get all the file names
however , If you need to get other information about the file , Such as length, etc , You need to use system calls
In some other systems , Even getting the file name requires a system call , For example, in MS-DOS This is the case in the system
Whether or not the implementation is related to the specific system , We need to provide a system independent access to file information

The following procedure will be adopted fsize Make that clear
fsize Procedure is ls A special form of command , It prints the length of all files specified in the command line parameter table
If one of the files is a directory , be fsize The program will recursively call itself... To this directory
If there are no arguments on the command line , be fsize The program handles the current directory

Let's first review UNIX The structure of the file system
stay UNIX In the system , A directory is a file , This file contains a list of file names and information indicating the location of these files
“ Location ” Is a table that points to other tables ( namely inode surface ) The index of
Of documents inode It is the place where all the file information except the file name is stored
Catalog items (directory entry) Usually contains only two entries : File name and inode Number

In different versions of the system , The format of the directory is different from the exact content
therefore , In order to isolate the non portable parts , We divided the task into two parts
The outer layer defines a called Dirent The structure and 3 A function opendirreaddir and closedir
They provide system independent pairs of names and... In directory entries inode Numbered access
We will use this interface to write fsize Program , And then explain how to communicate with Version 7 and System V UNIX These functions are implemented on systems with the same directory structure
Other situations are reserved for practice

structure Dirent contain inode Number and file name
The maximum length of a file name is determined by NAME_MAX Set up ,NAME_MAX The value of is determined by the system
opendir Return a point called DIR Pointer to the structure of , The structure and structure FILE similar , It will be readdir and closedir Use
All this information is stored in the header file dirent.h in

#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);

system call stat Take the file name as the parameter , Return the file's inode All information in , If something goes wrong , Then return to -1

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

It names the file name Of the file inode Information filling structure stbuf in
describe stat Structure definition of return value , The header file is included in <sys/stat.h> in
A typical form of this structure is shown below :

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 */ 
};

dev_t and ino_t And so on <sys/types.h> In the definition of , This file must be included in the program

st_mode Item contains a series of flags that describe the file , The signs are in <sys/stat.h> In the definition of
We only need to deal with the relevant parts of the file type :

#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 */ 
/* ... */

Let's start writing the program fsize
If by stat The pattern obtained by calling indicates that a file is not a directory , It is easy to get the length of the file , And output directly
however , If the file is a directory , The files in the directory must be processed one by one
Because this directory may contain subdirectories , So the process is recursive

The main program main Handling command line arguments , And pass each parameter to the function fsize

#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; 
}

function fsize Print the length of the file
however , If this file is a directory , be fsize First call dirwalk Function handles all the files it contains
Notice how to use the file <sys/stat.h> Flag name in S_IFMT and S_IFDIR To determine whether the file is a directory
Brackets are required , because & Operator takes precedence over == Operator precedence

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); 
}

function dirwalk Is a general-purpose function , It calls the function... For each file in the directory fcn once
It first opens the directory , Loop through each of these files , And call this function for each file , Then close the directory and return to
because fsize The function calls... For each directory dirwalk function , So these two functions are called recursively to each other

#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); 
}

Every time you call readdir Will return a pointer , It points to the information of the next file
If there are no pending files in the directory , This function will return NULL
Each directory contains itself . And parent directory .. Project , They must be skipped during processing , Otherwise it will lead to an infinite loop

Up to now , The code is independent of the format of the directory
The next step is to provide a on a specific system opendirreaddir and closedir The simplest version of
The following functions apply to Version 7 and System V UNIX System , They use header files <sys/dir.h> Directory information in :

#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' */ 
};

Some versions of the system support longer file names and more complex directory structures

type ino_t It's using typedef The type of definition , It is used to describe inode Index of tables
In the system we usually use , This type is unsigned short, But this information should not be used in the program
Because this type may be different in different systems , So use typedef The definition is better
be-all “ System ” Type can be found in file <sys/types.h> Find

opendir Function first opens the directory , Verify that this file is a directory ( Call system call fstat, It is associated with stat similar , But it takes the file descriptor as a parameter )
Then assign a directory structure , And save the information :

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; 
}

closedir Function to close a catalog file and free memory space :

/* closedir: close directory opened by opendir */ 
void closedir(DIR *dp) 
{
     
    if (dp) {
     
        close(dp->fd); 
        free(dp); 
    } 
}

Last , function readdir Use read The system call reads each directory entry
If a directory location is not currently in use ( Because a file was deleted ), Then it's inode The number is 0, And skip the position
otherwise , take inode Number and directory name are placed in one static In the structure of type , And return a pointer to this structure to the user
Every time you call readdir Function will overwrite the information obtained from the previous call

#include <sys/dir.h> /* local directory structure */

/* readdir: read directory entries in sequence */ 
Dirent *readdir(DIR *dp) 
{
     
    struct direct dirbuf; /* local directory structure */ 
    static Dirent d; /* return: portable structure */
    
    while (read(dp->fd, (char *) &dirbuf, sizeof(dirbuf)) == sizeof(dirbuf)) {
     
        if (dirbuf.d_ino == 0) /* slot not in use */ 
            continue; 
        d.ino = dirbuf.d_ino; 
        strncpy(d.name, dirbuf.d_name, DIRSIZ); 
        d.name[DIRSIZ] = '\0'; /* ensure termination */ 
        return &d; 
    } 
    return NULL; 
}

Even though fsize The program is very special , But it does illustrate some important ideas
First , Many programs are not “ System program ”, They only use information maintained by the operating system
For such a program , The important thing is , The representation of information only appears in the standard header file , The program that uses them only needs to include these header files in the file , There is no need to include the corresponding declaration
secondly , It is possible to create a system independent interface for system related objects
Functions in the standard library are a good example


Catalog 、 reference

原网站

版权声明
本文为[M rookie M]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/163/202206121615253515.html