简体   繁体   中英

How can I keep track of file path name in recursive function?

I currently have a function that will go through a directory and print every file in each directory.

void printdir(char *dir, int depth)
{
    DIR *dp;
    struct dirent *entry;
    struct stat statbuf;
    if ((dp = opendir(dir))==NULL)
    {
        fprintf(stderr, "cannot open director: %s\n", dir);
        return;
    }
    chdir(dir);
    while((entry = readdir(dp))!=NULL)
    {
        lstat(entry->d_name, &statbuf);
        if(S_ISDIR(statbuf.st_mode))
        {
            if(strcmp(".", entry ->d_name)==0 || strcmp("..", entry->d_name) ==0)
                continue;
            printf("directory %*s%s/\n", depth, "", entry->d_name);
            printdir(entry->d_name, depth+4, path);

        }
        else printf("file %*s/%s\n", depth, "",entry->d_name);
    }
    chdir("..");
    closedir(dp);
}

I need to keep track of the whole path name. I originally did this by having a malloc string

char *path = malloc(sizeof(char)*500); 

and then I strcat the original filename (obtained from user) to path. I then made path a parameter, so any time I opened a new directory, I would add the name to path. The only problem with this, is that I don't know when to 'reset' path if that makes sense. So if I have directoryA with directoryB,C,D, when I leave in directory B, I need to reset path to "./directoryA" and then add directory C. Basically if anyone could look at the code I have and see if there's a way to edit it so I could keep track of the file name, that would be very helpful! Thank you!

The stack is your friend, use it.
The example below uses the stack as means to keep reference of where you are.
the dir argument to printdir is now a copy.
Also - since you now have a full path, you no longer need to chdir anymore.
Finally, added goto bail instead of return to free the directory handle on error.

#define _POSIX_C_SOURCE 1
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <alloca.h>
#include <limits.h>

void printdir(char *dir, int depth)
{
    DIR *dp;
    struct dirent *entry;
    struct stat statbuf;
    if ((dp = opendir(dir))==NULL)
    {
        fprintf(stderr, "cannot open director: %s\n", dir);
        goto bail;
    }
    //chdir(dir);
    while((entry = readdir(dp))!=NULL)
    {            
        size_t dir_len = strlen(dir);
        size_t name_len = strlen(entry->d_name);
        char* child = (char*)alloca((dir_len + name_len + 1 /* our added '/' */ + 1 /* null termination */) * sizeof(char));
        if (child == NULL){
            fprintf(stderr,"out of stack memory");
            goto bail;
        }
        // Copy the current dir + new directory to 'child'.
        // Could use strcpy and then strcat instead
        memcpy(child,dir,dir_len);
        child[dir_len] = '/';
        memcpy(child + dir_len + 1,entry->d_name,name_len);
        child[dir_len + 1 + name_len] = '\0';

        lstat(child, &statbuf);
        if(S_ISDIR(statbuf.st_mode))
        {
            if(strcmp(".", entry ->d_name)==0 || strcmp("..", entry->d_name) ==0)
                continue;
            printf("directory %*s%s/\n", depth, "", child);
            printdir(child, depth+4);
        }
        else printf("file %*s/%s\n", depth, "",child);
    }
    //chdir("..");

bail:
    if (dp){
        closedir(dp);
    }
}

int main(int argc,char** argv){
    char* path = ".";
    printdir(path,0);
    return 0;
}

Technically, you do not have to use malloc (and remember to free, on return). You can use local array, or VLA (variable length arrays). Assuming 'fullpath' is the third argument to printdir, and assuming initial call will pass "" to fullpath.

void printdir(char *dir, int depth, char *fullpath)

Then

        if(S_ISDIR(statbuf.st_mode))
        {
            if(strcmp(".", entry ->d_name)==0 || strcmp("..", entry->d_name) ==0)
                continue;

            char newpath[500] ;     // auto variable, OR
            char path[strlen(fullpath) + strlen(entry->d_name) + 10) ;    // VLA
            snprintf(newpath, sizeof(newpath), "%s/%s", fullpath, entry->d_name)) ;

            psrintf("directory %*s%s/\n", depth, "", entry->d_name);
            printdir(entry->d_name, depth+4, newpath);

        }

Since you are doing DFS you can simply do as below without adding new argument as said by @dash-o.

if(S_ISDIR(statbuf.st_mode))
{
    if(strcmp(".", entry ->d_name)==0 || strcmp("..", entry->d_name) ==0)
        continue;

    char newpath[500] ;     // auto variable, OR
    char path[strlen(dir) + strlen(entry->d_name) + 10) ;    // VLA
    snprintf(newpath, sizeof(newpath), "%s/%s", dir, entry->d_name)) ;

    psrintf("directory %*s%s/\n", depth, "", entry->d_name);
    printdir(newpath, depth+4);

}

Also remove, you don't need it anymore.

chdir(dir);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM