尝试递归循环文件夹内容

发布于 2024-10-09 15:05:50 字数 5994 浏览 0 评论 0原文

我正在尝试创建一个递归返回文件夹内容的函数。在测试时,我遇到了一些问题。对于某些文件夹,它可以工作,对于其他文件夹,它给我一个 EXC_BAD_ACCESS,有时它会停止。

我已经尝试用GDB调试它很长一段时间了,但我就是找不到解决我的问题的方法。该函数并不简单,如下所示。

#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>

/* THIS IS AN EXTRACT FROM A LARGER FILE
 THERE MAY BE TO MUCH INCLUDE STATEMENTS
 */

struct Directory {
    DIR *handle;
    const char *filename;
};
typedef struct Directory Directory;

int DirectoryCreate(const char *n, Directory *d) {
    DIR *dh;
    char *str;

    dh = opendir(n);
    if(dh == NULL) {
        return -1;
    }

    d->handle = dh;
    str = malloc(strlen(n) + 1);
    if(str == NULL) {
        errno = ENOMEM;
        closedir(d->handle);
        return -1;
    }
    strcpy(str, n);
    d->filename = (const char *)str;
    return 0;
}

void DirectoryFree(Directory *s) {
    if(s->handle) {
        closedir(s->handle);
    }
    if(s->filename) {
        free((void *)s->filename);
    }
}

void FreeDirectoryArray(Directory *array, size_t size) {
    register size_t i;
    for(i = 0; i < size; i++) {
        DirectoryFree(&(array[i]));
    }

    free(array);
}

Directory *ReadFolders = NULL;
size_t ReadFoldersSize = 0;
const char *ReadFolderFilename = NULL;
const char *ReadNextRecursiveItemInFolder(const char *folder) {
    struct dirent *entry;
    struct stat fileStatus;
    int status;
    mode_t mode;
    const char *newFilename;
    char *fullName;
    char *ptr;
    size_t strLen;

    if(folder == NULL && ReadFolders == NULL) {
        errno = 0;
        return NULL;
    }

    if(folder != NULL) {
        /* free the previous directory list */
        FreeDirectoryArray(ReadFolders, ReadFoldersSize);
        ReadFolders = NULL;
        ReadFoldersSize = 0;

        /* open the new directory */
        ReadFolders = (Directory *)realloc(ReadFolders, sizeof(Directory));
        ReadFoldersSize++;
        status = DirectoryCreate(folder, ReadFolders);
        if(status != 0) {
            FreeDirectoryArray(ReadFolders, ReadFoldersSize-1);
            ReadFolders = NULL;
            return NULL;
        }
    }

    entry = readdir(ReadFolders[ReadFoldersSize - 1].handle);
    /* If NULL, go to previous folder */
    if(entry == NULL) {
        DirectoryFree(&(ReadFolders[ReadFoldersSize - 1]));
        --ReadFoldersSize;

        /* if it's empty, we've reached the end */
        if(ReadFoldersSize == 0) {
            free(ReadFolders);
            ReadFolders = NULL;
            errno = 0;
            return NULL;
        }

        newFilename = ReadNextRecursiveItemInFolder(NULL);
        return newFilename;
    }
    /* Make sure the entry name is not . or .. */
    if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
        newFilename = ReadNextRecursiveItemInFolder(NULL);
        return newFilename;
    }

    /* we've got an entry, now construct the full path */
    strLen = 
        strlen(ReadFolders[ReadFoldersSize - 1].filename) +
        1 +
        strlen(entry->d_name);
    fullName = malloc(strLen + 1);
    ptr = fullName;
    strcpy(ptr, ReadFolders[ReadFoldersSize - 1].filename);
    ptr += strlen(ReadFolders[ReadFoldersSize - 1].filename);
    strcpy(ptr, "/");
    ptr++;
    strcpy(ptr, entry->d_name);
    newFilename = fullName;

    /* no recurse on symbolic links */
    status = lstat(newFilename, &fileStatus);
    if(status != 0) {
        FreeDirectoryArray(ReadFolders, ReadFoldersSize);
        ReadFolders = NULL;
        ReadFoldersSize = 0;
        return NULL;
    }

    mode = fileStatus.st_mode;
    /* if not readable for file or not searchable for folder, get next */
    /* if folder and not link, recursively continue */
    /* else  return the new name */
    if((((mode & S_IFDIR) == S_IFDIR) && (mode & S_IXUSR) != S_IXUSR) || 
        (mode & S_IRUSR) != S_IRUSR) {
        free((void *)newFilename);
        newFilename = ReadNextRecursiveItemInFolder(NULL);
        return newFilename;

    } else if((mode & S_IFDIR) && (mode & S_IFLNK) != S_IFLNK) {
        ReadFolders = realloc(ReadFolders, ReadFoldersSize + 1);
        ReadFoldersSize++;
        errno = 0;
        status = DirectoryCreate(newFilename, &(ReadFolders[ReadFoldersSize - 1]));
        if(status != 0) {
            FreeDirectoryArray(ReadFolders, ReadFoldersSize - 1);
            ReadFolders = NULL;
            ReadFoldersSize = 0;
            return NULL;
        }

        if(newFilename != ReadFolderFilename) {
            free((void *)ReadFolderFilename);
            ReadFolderFilename = newFilename;
        }

    } else {
        if(newFilename != ReadFolderFilename) {
            free((void *)ReadFolderFilename);
            ReadFolderFilename = newFilename;
        }
        errno = 0;
    }

    return ReadFolderFilename;
}

int main() {
    const char *filename = "/Users/";
    const char *entry;

    while(1) {
        entry = ReadNextRecursiveItemInFolder(filename);
        filename = NULL;
        if(entry == NULL) {
            if(errno == 0) {
                printf("End reached\n");
            } else {
                printf("Error: %s\n", strerror(errno));
            }
            break;
        }

        printf("Entry: %s\n", entry);
    }

    return 0;
}

我将简要解释该代码的工作原理。要开始循环目录,您必须为函数提供完整的目录路径。所有后续调用都必须传递 NULL 才能获取行中的下一个项目,除非它们想要处理另一个目录。

该代码递归地计算文件夹中的每个文件和文件夹。它不遵循符号链接,并且仅计算可读文件和可执行目录。为了跟踪其“流程”,该函数使用 3 个全局变量:

  • ReadFolders:一个 Directory 结构数组,用于跟踪不同级别的文件夹。最后一张在后面。
  • ReadFoldersSize:ReadFolders 中 Directory 结构的数量。
  • ReadFolderFilename:包含最后处理的项目的字符串。

我希望我能在这里找到一些帮助 ief2。

I'm trying to create a function which recursively returns the contents of a folder. While testing it, I encounter some problems. With some folders it works, With others it gives me an EXC_BAD_ACCESS, and sometimes it just stops.

I have been trying to debug it with GDB for a long time, but I just can't find a solution to my problem. The function is not quit short an goes as follows.

#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>

/* THIS IS AN EXTRACT FROM A LARGER FILE
 THERE MAY BE TO MUCH INCLUDE STATEMENTS
 */

struct Directory {
    DIR *handle;
    const char *filename;
};
typedef struct Directory Directory;

int DirectoryCreate(const char *n, Directory *d) {
    DIR *dh;
    char *str;

    dh = opendir(n);
    if(dh == NULL) {
        return -1;
    }

    d->handle = dh;
    str = malloc(strlen(n) + 1);
    if(str == NULL) {
        errno = ENOMEM;
        closedir(d->handle);
        return -1;
    }
    strcpy(str, n);
    d->filename = (const char *)str;
    return 0;
}

void DirectoryFree(Directory *s) {
    if(s->handle) {
        closedir(s->handle);
    }
    if(s->filename) {
        free((void *)s->filename);
    }
}

void FreeDirectoryArray(Directory *array, size_t size) {
    register size_t i;
    for(i = 0; i < size; i++) {
        DirectoryFree(&(array[i]));
    }

    free(array);
}

Directory *ReadFolders = NULL;
size_t ReadFoldersSize = 0;
const char *ReadFolderFilename = NULL;
const char *ReadNextRecursiveItemInFolder(const char *folder) {
    struct dirent *entry;
    struct stat fileStatus;
    int status;
    mode_t mode;
    const char *newFilename;
    char *fullName;
    char *ptr;
    size_t strLen;

    if(folder == NULL && ReadFolders == NULL) {
        errno = 0;
        return NULL;
    }

    if(folder != NULL) {
        /* free the previous directory list */
        FreeDirectoryArray(ReadFolders, ReadFoldersSize);
        ReadFolders = NULL;
        ReadFoldersSize = 0;

        /* open the new directory */
        ReadFolders = (Directory *)realloc(ReadFolders, sizeof(Directory));
        ReadFoldersSize++;
        status = DirectoryCreate(folder, ReadFolders);
        if(status != 0) {
            FreeDirectoryArray(ReadFolders, ReadFoldersSize-1);
            ReadFolders = NULL;
            return NULL;
        }
    }

    entry = readdir(ReadFolders[ReadFoldersSize - 1].handle);
    /* If NULL, go to previous folder */
    if(entry == NULL) {
        DirectoryFree(&(ReadFolders[ReadFoldersSize - 1]));
        --ReadFoldersSize;

        /* if it's empty, we've reached the end */
        if(ReadFoldersSize == 0) {
            free(ReadFolders);
            ReadFolders = NULL;
            errno = 0;
            return NULL;
        }

        newFilename = ReadNextRecursiveItemInFolder(NULL);
        return newFilename;
    }
    /* Make sure the entry name is not . or .. */
    if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
        newFilename = ReadNextRecursiveItemInFolder(NULL);
        return newFilename;
    }

    /* we've got an entry, now construct the full path */
    strLen = 
        strlen(ReadFolders[ReadFoldersSize - 1].filename) +
        1 +
        strlen(entry->d_name);
    fullName = malloc(strLen + 1);
    ptr = fullName;
    strcpy(ptr, ReadFolders[ReadFoldersSize - 1].filename);
    ptr += strlen(ReadFolders[ReadFoldersSize - 1].filename);
    strcpy(ptr, "/");
    ptr++;
    strcpy(ptr, entry->d_name);
    newFilename = fullName;

    /* no recurse on symbolic links */
    status = lstat(newFilename, &fileStatus);
    if(status != 0) {
        FreeDirectoryArray(ReadFolders, ReadFoldersSize);
        ReadFolders = NULL;
        ReadFoldersSize = 0;
        return NULL;
    }

    mode = fileStatus.st_mode;
    /* if not readable for file or not searchable for folder, get next */
    /* if folder and not link, recursively continue */
    /* else  return the new name */
    if((((mode & S_IFDIR) == S_IFDIR) && (mode & S_IXUSR) != S_IXUSR) || 
        (mode & S_IRUSR) != S_IRUSR) {
        free((void *)newFilename);
        newFilename = ReadNextRecursiveItemInFolder(NULL);
        return newFilename;

    } else if((mode & S_IFDIR) && (mode & S_IFLNK) != S_IFLNK) {
        ReadFolders = realloc(ReadFolders, ReadFoldersSize + 1);
        ReadFoldersSize++;
        errno = 0;
        status = DirectoryCreate(newFilename, &(ReadFolders[ReadFoldersSize - 1]));
        if(status != 0) {
            FreeDirectoryArray(ReadFolders, ReadFoldersSize - 1);
            ReadFolders = NULL;
            ReadFoldersSize = 0;
            return NULL;
        }

        if(newFilename != ReadFolderFilename) {
            free((void *)ReadFolderFilename);
            ReadFolderFilename = newFilename;
        }

    } else {
        if(newFilename != ReadFolderFilename) {
            free((void *)ReadFolderFilename);
            ReadFolderFilename = newFilename;
        }
        errno = 0;
    }

    return ReadFolderFilename;
}

int main() {
    const char *filename = "/Users/";
    const char *entry;

    while(1) {
        entry = ReadNextRecursiveItemInFolder(filename);
        filename = NULL;
        if(entry == NULL) {
            if(errno == 0) {
                printf("End reached\n");
            } else {
                printf("Error: %s\n", strerror(errno));
            }
            break;
        }

        printf("Entry: %s\n", entry);
    }

    return 0;
}

I'll give a brief explanation how the code works. To start looping a directory, you have to give the full directory path to the function. All subsequent calls have to pass NULL to get the next item in line, unless they want to process another directory.

The code counts every file and folder in a folder, recursively. It does not follow symbolic links, and it only counts readable files and executable directories. To keep track of its 'flow', the function uses 3 global variables:

  • ReadFolders: an array of Directory structures used to keep track of different levels of folders. The last one at the back.
  • ReadFoldersSize: the amount of Directory structures in ReadFolders.
  • ReadFolderFilename: the string that contains the last item processed.

I hope I can find some help around here,
ief2.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

萌面超妹 2024-10-16 15:05:50

realloc 大小是错误的:它不是“n”而是“n*size”。

所以第 153 行应该是:

ReadFolders = realloc(ReadFolders, (ReadFoldersSize + 1)*sizeof(Directory));

The realloc size is wrong : it not "n" but "n*size".

So the line 153 should be :

ReadFolders = realloc(ReadFolders, (ReadFoldersSize + 1)*sizeof(Directory));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文