apue 4.22 递归目录

4 篇文章 0 订阅
这个程序遍历指定的文件路径,统计其中不同类型的文件数量,包括常规文件、目录、块设备、字符设备、FIFO、符号链接和套接字。通过调用自定义的myfunc函数处理每个文件并进行计数,最终输出各类文件的百分比。
摘要由CSDN通过智能技术生成
#include "apue.h"
#include <dirent.h>
#include <limits.h>

/* 为每个文件名调用的函数类型 */
typedef int     Myfunc(const char *, const struct stat *, int);

static Myfunc   myfunc;
static int              myftw(char *, Myfunc *);
static int              dopath(Myfunc *);

static long     nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot;

int
main(int argc, char *argv[])
{
        int             ret;

        if (argc != 2)
                err_quit("usage:  ftw  <starting-pathname>");

        ret = myftw(argv[1], myfunc);           /* play */

        ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;// 所有文件的总数
        if (ntot == 0)
                ntot = 1;               /* 避免没有任何文件后*0。(这句有点多余的感觉,为了防止该程序执行的时候另一个root rm -rf *???) */
        // 以下是数量和占比
        printf("regular files  = %7ld, %5.2f %%\n", nreg,
          nreg*100.0/ntot);
        printf("directories    = %7ld, %5.2f %%\n", ndir,
          ndir*100.0/ntot);
        printf("block special  = %7ld, %5.2f %%\n", nblk,
          nblk*100.0/ntot);
        printf("char special   = %7ld, %5.2f %%\n", nchr,
          nchr*100.0/ntot);
        printf("FIFOs          = %7ld, %5.2f %%\n", nfifo,
          nfifo*100.0/ntot);
        printf("symbolic links = %7ld, %5.2f %%\n", nslink,
          nslink*100.0/ntot);
        printf("sockets        = %7ld, %5.2f %%\n", nsock,
          nsock*100.0/ntot);
        exit(ret);
}

/*
 * Descend through the hierarchy, starting at "pathname".
 * The caller's func() is called for every file.
 *
 * 从"pathname"开始,降序遍历每个目录
 * 为每个文件调用func()
 */
#define FTW_F   1               /* 目录以外的文件 */
#define FTW_D   2               /* 目录 */
#define FTW_DNR 3               /* 无法读取的目录 */
#define FTW_NS  4               /* 无法统计的文件 */

static char     *fullpath;              /* 包含每个文件的完整路径名 */
static size_t pathlen;  /* unsigned int */


// 主要处理pathname
static int                                      /* 我们返回func() 返回的任何内容 */
myftw(char *pathname, Myfunc *func)
{
        fullpath = path_alloc(&pathlen);        /* 为存储路径的变量(fullpath)开辟内存 */ /* malloc PATH_MAX+1 bytes */
                                                                                /* ({程序路径分配}) */
        // size_t 是否能容下 strlen(pathname)
        if (pathlen <= strlen(pathname)) {
                pathlen = strlen(pathname) * 2;
                if ((fullpath = realloc(fullpath, pathlen)) == NULL)
                        err_sys("realloc failed");
        }

        strcpy(fullpath, pathname);     // 路径名复制到fullpath
        /* 程序运行到这myftw 只是负责了 路径名存储 */

        // func 传入时是空的
        return(dopath(func));// 直接返回dopath传递过来的ret
}

/*
 * Descend through the hierarchy, starting at "fullpath".
 * If "fullpath" is anything other than a directory, we lstat() it,
 * call func(), and return.  For a directory, we call ourself
 * recursively for each name in the directory.
 *
 * 从 “fullpath” 开始,通过层次结构下降。
 * 如果 “fullpath” 不是目录,我们lstat()它,
 * 调用func(),然后返回。 对于目录,我们称自己为
 * 递归地针对目录中的每个名称。 
 */
// 核心函数
static int /* 我们返回 func() 返回的任何内容 */
dopath(Myfunc* func)
{
        struct stat             statbuf;
        struct dirent   *dirp;
        DIR                             *dp;
        int                             ret, n;

        // 获取fullpath保存的路径名的文件stat
        if (lstat(fullpath, &statbuf) < 0)      /* 获取文件stat失败 */
                return(func(fullpath, &statbuf, FTW_NS));       // 获取stat失败传入无法统计的标志和路径和stat结构体到myfunc。进入func后发出error退出dopath

        if (S_ISDIR(statbuf.st_mode) == 0)      /* 不是目录 */
                return(func(fullpath, &statbuf, FTW_F));// 处理目录以外的文件,并计数后退出dopath

        /*
         * 它是一个目录。才会执行以下代码
         * 首先为目录调用 func(),然后处理目录中的每个文件名 
         */
        if ((ret = func(fullpath, &statbuf, FTW_D)) != 0)
                return(ret);// 是目录并且func return非正常才会执行该行直接退出dopath

        // n sava path_name_len
        n = strlen(fullpath);
        if (n + NAME_MAX + 2 > pathlen) {// 拓展n长度后是否大于pathlen(当前fullpath也是和pathlen一样)
                // n拓展后的长度大于pathlen长度,就把pathlen长度*2。再为fullpath拓展,不然n > fullpath长度就会溢出
                pathlen *= 2;
                if ((fullpath = realloc(fullpath, pathlen)) == NULL)
                        err_sys("realloc failed");
        }
        fullpath[n++] = '/';// 路径名后补/后n+1
        fullpath[n] = 0;// 最后补0(注意上一句是n++)

        // 打开遇到的那个目录
        if ((dp = opendir(fullpath)) == NULL)   /* 无法读取目录 */
                return(func(fullpath, &statbuf, FTW_DNR));// 无法打开计数+1后退出dopath


        while ((dirp = readdir(dp)) != NULL) {
                if (strcmp(dirp->d_name, ".") == 0  ||// strcmp字符串比较,忽略.和..
                    strcmp(dirp->d_name, "..") == 0)
                                continue;               /* 忽略.和.. */

                strcpy(&fullpath[n], dirp->d_name);     /* 在“/”后面追加当前打开的目录名称(注意前面fullpath[n++]和fullpath[n]这两句!!!够细) */
                if ((ret = dopath(func)) != 0)          /* 递归目录 */
                        break;  /* 退出循环 */
        }

        // n记录了斜杠后面0的位置,[n-1]就到斜杠了。假设路径: /home/hyl(斜杠记录的是h的位置)
        fullpath[n-1] = 0;      /* 从斜线开始删除所有内容 */

        // 关闭目录
        if (closedir(dp) < 0)
                err_ret("can't close directory %s", fullpath);

        // 向上(myftw)传递ret
        return(ret);
}

// 统计文件个数和处理stat error
static int
myfunc(const char *pathname, const struct stat *statptr, int type)
{
        switch (type) {
        case FTW_F:// 目录以外的文件
                switch (statptr->st_mode & S_IFMT) {
                case S_IFREG:   nreg++;         break;
                case S_IFBLK:   nblk++;         break;
                case S_IFCHR:   nchr++;         break;
                case S_IFIFO:   nfifo++;        break;
                case S_IFLNK:   nslink++;       break;
                case S_IFSOCK:  nsock++;        break;
                case S_IFDIR:   /* 目录应该有的类型 = FTW_D */
                        err_dump("for S_IFDIR for %s", pathname);
                }
                break;
        case FTW_D:// 目录
                ndir++;
                break;
        case FTW_DNR:// 无法读取的目录
                err_ret("can't read directory %s", pathname);
                break;
        case FTW_NS:// 无法统计的文件
                err_ret("stat error for %s", pathname);
                break;
        default:
                err_dump("unknown type %d for pathname %s", type, pathname);
        }
        return(0);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值