Retrieving full path to file while recursively moving C file
I am doing a recursive walk through directories to make changes to files. My change file function requires the full file path to be able to do things. However, what my program is doing right now is just getting the name of the current file or folder, but not the full path.
My approach is that I will create a string and add names to it until I get the full path. However, since I am doing the recursion I am having trouble passing the string to add more lines to it.
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
void recursiveWalk(const char *pathName, char *fullPath, int level) {
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(pathName))) {
fprintf(stderr, "Could not open directory\n");
return;
}
if (!(entry = readdir(dir))) {
fprintf(stderr, "Could not read directory\n");
return;
}
do {
if (entry->d_type == DT_DIR) { // found subdirectory
char path[1024];
int len = snprintf(path, sizeof(path)-1, "%s/%s", pathName, entry->d_name); // get depth
path[len] = 0;
// skip hidden paths
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
fprintf(stdout, "%*s[%s]\n", level*2, "", entry->d_name);
// Append fullPath to entry->d_name here
recursiveWalk(path, fullPath, level + 1);
}
else { // files
fprintf(stdout, "%*s- %s\n", level*2, "", entry->d_name);
//changeFile(fullPath);
}
} while (entry = readdir(dir));
closedir(dir);
}
int main(int argn, char *argv[]) {
int level = 0;
recursiveWalk(".", "", level);
return 0;
}
source to share
There are tons of problems in your code.
- you never use or change
fullPath
inrecursiveWalk
- your formats are weird: you use
level*2
to limit the number of characters printed from an empty line - you only compute the actual path if you find the directory, while you say you need to modify it in the file.
- you add
path[len] = 0
aftersnprintf
when itsnprintf
guarantees that but the buffer ends at zero
But also, you are correctly passing the path to the parsed dir append to the path passed in the initial call, but in a variable pathName
and evaluates as path
.
So a possible fix for your code would be:
- fix formats for printf
- remove the unused parameter
fullPath
fromrecursiveWalk
- allways compute
path
and use it in the file branch - comment out the unnecessary
path[len] = '\0'
- I also replaced
while (entry = readdir(dir));
withwhile ((entry = readdir(dir)));
to tell the compiler explicitly that I want to set the entry and then check its value - and remove the warning
Possible code:
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
void recursiveWalk(const char *pathName, int level) {
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(pathName))) {
fprintf(stderr, "Could not open directory\n");
return;
}
if (!(entry = readdir(dir))) {
fprintf(stderr, "Could not read directory\n");
return;
}
do {
char path[1024];
int len = snprintf(path, sizeof(path)-1, "%s/%s", pathName, entry->d_name); // get depth
// path[len] = 0;
if (entry->d_type == DT_DIR) { // found subdirectory
// skip hidden paths
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
fprintf(stdout, "%s [%s] (%d)\n", pathName, entry->d_name, level);
// Append fullPath to entry->d_name here
recursiveWalk(path, level + 1);
}
else { // files
fprintf(stdout, "%s (%d)\n", path, level);
//changeFile(fullPath);
}
} while ((entry = readdir(dir)));
closedir(dir);
}
int main(int argn, char *argv[]) {
int level = 0;
recursiveWalk(".", level);
return 0;
}
source to share
Recursion is a concise way of expressing things (especially pedestrian directories), but in reality you should generally avoid it in practice. If the directory tree is deep enough it will crash your software.
Using a queue eliminates the need for recursion and is generally an efficient way to move around.
I am including the code I am using to process the directory tree in the project
static int on_dir(const char* const dir, struct duplicate** dp) {
bool r = opts.recurse;
DIR* d = opendir(dir);
if (!d)
return - 1;
struct dirent* de;
while ((de = readdir(d))) {
struct stat s;
size_t bs = strlen(dir) + strlen(de->d_name) + 2;
char b[bs];
const char* const a = strjoin(b, dir, de->d_name, '/');
if (lstat(a, &s)) {
print_error("unable to stat %s", d);
continue;
}
if (S_ISREG(s.st_mode))
if (on_file(a, &s, dp))
print_error("unable to process file %s/%s", dir, de->d_name);
}
if (!r) {
if (closedir(d))
on_fatal("unable to close directory %s", dir);
return 0;
}
rewinddir(d);
while ((de = readdir(d))) {
struct stat ds;
size_t bs = strlen(dir) + strlen(de->d_name) + 2;
char b[bs];
const char* const d = strjoin(b, dir, de->d_name, '/');
if (lstat(d, &ds)) {
print_error("unable to stat %s", d);
continue;
}
if (S_ISDIR(ds.st_mode)) {
const char* const dot = ".";
const char* const dotdot = "..";
if (!strcmp(dot, de->d_name) || !strcmp(dotdot, de->d_name))
continue;
struct path* p = path_create(strcpy(fmalloc(bs), d));
queue_add(&paths, &p->queue);
}
}
if (closedir(d))
print_error("unable to close directory %s", dir);
return 0;
}
and the code for strjoin
static inline char* strjoin(char* restrict const d, const char* restrict const a, const char* restrict const b, const char c) {
size_t na = strlen(a);
size_t nb = strlen(b);
memcpy(d, a, na);
d[na] = c;
memcpy(d + na + 1, b, nb);
d[na + nb + 1] = '\0';
return d;
}
I hope this helps. Please feel free to use whatever code you find in the git repository.
source to share