C语言实现文件类型统计函数

#include<dirent.h>
#include<limits.h>
#include<sys/stat.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

#define FTW_F        1        //标记非目录文件
#define FTW_D        2        //标记目录文件
#define FTW_DNR        3        //标记不可读目录
#define FTW_NS        4        //标记不可获得stat的文件

static char *fullpath;        //保存文件的全路径
static size_t pathlen;        //保存文件的路径长度


//定义处理文件的函数
typedef int Myfunc(const char *,const struct stat*,int);
static Myfunc myfunc;
static int myftw(char *,Myfunc *);
static int dopath(Myfunc *);
char *path_alloc(size_t *size_t);

/*
nreg:普通文件的个数;    ndir: 目录文件的数量;    nblk:块特殊文件的数量
nchr:字符特殊文件的数量    nfifo:管道特殊文件的数量
nslink:符号连接特殊文件的数量;    nsock:套接字文件数量; ntot:总文件数量
*/
static long nreg,ndir,nblk,nchr,nfifo,nslink,nsock,ntot;

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

    if(argc != 2)
    {
        printf("falut command input !\n");
        exit(1);
    }

    //计算各类文件的个数
    ret = myftw(argv[1],myfunc);

    ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;

    //避免除以0
    if(ntot == 0)
    {
        ntot = 1;
    }

    printf("regular files = %7ld,%5.2f %%\n",nreg,nreg*100.0 / ntot);
    printf("direciotn files = %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("sockers  = %7ld,%5.2f %%\n",nsock,nsock*100.0 / ntot);
    
}

//处理pathname并保存在一个全局的字符数组中,调用dopath
static int myftw(char *pathname,Myfunc *func)
{
    //为保存路径的字符串数组分配空间
    fullpath = path_alloc(&pathlen);
    
    //如果分配内存空间不够就重新分配
    if(pathlen <= strlen(pathname))
    {
        pathlen = strlen(pathname) * 2;
        if((fullpath = realloc(fullpath,pathlen )) == NULL);
            printf("realloc failed\n");
    }
    //将路径名参数保存到全路径中,fullpath是全局变量,dopath函数可以调用
    strcpy(fullpath,pathname);
    
    return (dopath(func));
}

//路径数组分配
char *path_alloc(size_t *size)
{
    char *p = NULL;
    if(!size)
        return NULL;
    p = malloc(256);
    if(p)
        *size = 256;
    else
        *size = 0;

    return p;
}

//dopath用于判断是否是目录,然后根据选择情况是直接进入myfun函数取技术
//还是递归调用dopath函数
static int dopath(Myfunc *func)
{
    struct stat        statbuf;
    struct dirent    *dirp;
    DIR             *dp;
    int             ret,n;

    //调用lstat获取路径名的stat信息,如果不成功,调用func函数,并传递给FTW_NS
    if(lstat(fullpath,&statbuf) < 0)
        return (func(fullpath, &statbuf, FTW_NS));

    //查看文件stat结构的st_mode,如果不是目录,调用func函数 
    //并传递给FTW_F,交由myfun进一步判断文件类型 
    if(S_ISDIR(statbuf.st_mode) == 0)
        return(func(fullpath,&statbuf,FTW_F));
    
    //最后一种情况就是该路径名代表的是一个目录,此次的fun的正常情况返回0
    //所以执行完func后还不会返回,会继续执行func
    if((ret = func(fullpath,&statbuf,FTW_D)) != 0)
        return(ret);
    
    //路径处理,扩充路径空间长度
    n = strlen(fullpath);
    if(n + NAME_MAX + 2 > pathlen)
    {
        pathlen *= 2;
        if((fullpath = realloc(fullpath,pathlen)) == NULL)
        {
            printf("realoc failed\n");
        }
    }
    
    fullpath[n++] = '/';
    fullpath[n] = 0;
    
    //处理每个目录项
    if((dp = opendir(fullpath)) == NULL)
        return (func(fullpath,&statbuf,FTW_DNR));
    while((dirp = readdir(dp)) != NULL)
    {
        //忽略当前目录(.)和上一级目录(..)以避免进入死循环
        if(strcmp(dirp->d_name,".") == 0 ||     
            strcmp(dirp->d_name,"..") == 0 )
            continue;
        strcpy(&fullpath[n],dirp->d_name); //在“/”之后加上当前目录项的命中

        if((ret = dopath(func)) != 0) //然后采用新的路径名递递归的调用dopath
            break;
    }

    fullpath[n-1] = 0;

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

}

//通过stat结构的st_mode字段来判断文件的类型,并计数
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:    
                        printf("for S_IFDIR for %s",pathname);
            }
            break;
            
        //对于目录文件进行处理
        case FTW_D:
            ndir++;
            break;
        //对于不可读目录进行处理
        case FTW_DNR:
            printf("%s dir isn't read",pathname);
            break;
        case FTW_NS:
            printf("%s stat error",pathname);

        default:
            printf("%d type aren't identified, path is %s",type,pathname);
    }

    return 0;
}

 

posted @ 2019-09-29 15:34  王清河  阅读(1111)  评论(0编辑  收藏  举报