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;
}

      

+3


source to share


2 answers


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

    after snprintf

    when it snprintf

    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));

    with while ((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;
}

      

+1


source


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.

+1


source







All Articles