#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);
}
apue 4.22 递归目录
最新推荐文章于 2023-04-26 16:05:09 发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)