gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS homebrew announcements > to understand recursive, you must first understand recursive

#151656 - darkchild - Sun Mar 02, 2008 12:34 am

ok guys, I'm going for a recursive folder killer, this is what I've done so far:

Code:
void recursivedelete(const char *folder, const char *dir)
{
   DIR_ITER* recur;
   bool temp;
   
   char *fullpath = " ";
   sprintf(fullpath, "%s%s/", dir, folder);
   recur = diropen (fullpath);
   if (recur != NULL)
   {
      while (dirnext(recur, filename, &st) == 0)
      {
         if ((strcmp(filename, ".") !=0) & (strcmp(filename, "..") !=0))
         {
            temp = (st.st_mode & S_IFDIR ? true : false);
            if (temp == true) {
               sprintf(fullpath, "%s%s/", dir, folder);

               recursivedelete(filename, fullpath );
               unlink(fullpath);
            }
            else
            {
               sprintf(fullpath, "%s%s/%s", dir, folder, filename);
               PA_OutputText(1,0,i +1, "%d : %s",i, fullpath);
               remove (fullpath );
            }
         }
      }
      sprintf(fullpath, "%s%s/", dir, folder);
      unlink(fullpath);
   }
}


yet all it does is go to that folder, delete all files in it, but not the subfolders.. not the subfolder's files.

now, the syntax of this function:

recursivedelete ("currentdir", "nameofthefolderwearedeleting");


what am I doing wrong? :X



Edit : I posted this on the wrong section, can a mod move it to the right one? :X

#151681 - zAlbee - Mon Mar 03, 2008 6:58 am

Well looks like syntax is reversed and should be

recursivedelete ("nameofthefolderwearedeleting", "currentdir");

Other than that, where are you allocating buffer space for your filename and fullpath strings? I don't even see filename declared.

Edit:
Two args for folder and dir seems unnecessary. You always use them together anyway. Line 19: unlink(fullpath) is unnecessary, because the recursive call will already have unlinked it.

Here's pseudocode that makes it easier to see.

Code:
// precondition: dir is a directory with 0 or more files
// postcondition: dir is gone, along with contents

rdel (dir) {
   for files f in dir:
      if (f is a folder) rdel(f)
      else remove (f)
   unlink (dir);
}

_________________
DS Lite white, R4DS, 1GB Kingmax microSD

#151688 - elwing - Mon Mar 03, 2008 8:55 am

not only are the 2 string not necessary since they are always used together, but worst you are concatenating the strings 3 time for each function call... by the way, wouldn't it be wise to add a recursive counter to avoid stack overflow? trough i'm not sure it's needed on a ds...

and to answers zalbee, yes there is no declaration for the strings, he's trying to construct fullpath right on the constant " " he declared... it's strange that the DS doesn't crash with that. on windows you'll get some nice access violation for that...

#151698 - Cydrak - Mon Mar 03, 2008 4:09 pm

elwing, that's because PCs have a full MMU; the DS hasn't, so writing const data will silently change the const. It will catch wildly out of range pointers though. In libnds, defaultExceptionHandler() installs a guru meditation/RSo'D.

Also... the stack maxes out at 16kb, which could really be a problem. libfat is moving toward UTF-8, so iirc it needs 3*256 chars. But, maybe you could build the path in place:
Code:
// trailing slash on 'dir' is required
rdel(dir) {
   char path[MAXPATHLEN]
   strcpy(path, dir)
   rdel_internal(path)
}

rdel_internal(dir) {
   for files f in dir:
      if (f not folder)
         remove (f)
      else
          tail = dir + strlen(dir)
          sprintf(tail, "%s/", f)
          rdel_internal(dir)
          strcpy(tail, "")
   unlink(dir)
}

darkchild, if you come from other languages, realise there are no dynamic "strings" in C... you need to make space ahead of time. If you don't give a big enough char array to sprintf(), it will happily blow past the end. Unless you can predict the size, snprintf() is better. It truncates, but better than crashing..

Pointers and arrays have an insidious tendency to look the same, but really they aren't (heh, someone correct me if I'm wrong):
Code:
char *fooPtr     = "foo";
char barArray[]  = "bar";
char bazArray[4] = { 'b','a','z', 0 };

The bar and baz definitions are arrays. One is just convenient (but deceptive) shorthand.

Whereas, foo only gives you a pointer *to* an array, and here, not one you should write to. It'd be like if you said "int* numPtr = &5;"... which is wrong. Since even if GCC let you do that, it may combine all the unique constants, and then you might accidentially change all the 5's in your code.


Last edited by Cydrak on Mon Mar 03, 2008 4:29 pm; edited 1 time in total

#151699 - masscat - Mon Mar 03, 2008 4:29 pm

Sometimes the best thing about recursion is not using it. Here is an iterative directory tree delete:

Code:
int
deleteDirTree( const char *aDirPath) {
  int success = 1;
  int complete = 0;
  uint32_t depth = 0;
  char next_dir_name[256];
  char orig_working_directory[MAXPATHLEN];

  getcwd( orig_working_directory, MAXPATHLEN);

  if ( chdir( aDirPath) != 0) {
    /* failed to get into the directory, cannot continue */
    return 0;
  }


  while ( !complete) {
    /* attempt to delete everything in the current directory */
    DIR_ITER *dp = diropen( ".");

    if ( dp != NULL) {
      int move_down = 0;
      struct stat file_st;
      char name_buffer[256];

      while (!complete) {
        if ( dirnext( dp, name_buffer, &file_st) == 0) {
          /*
           * avoid trying to delete "." and ".."
           */
          if ( strcmp( name_buffer, "..") != 0 && strcmp( name_buffer, ".") != 0) {
            if ( unlink( name_buffer) != 0) {
              if ( file_st.st_mode & S_IFDIR) {
                if ( errno == EPERM) {
                  /* the directory is not empty */
                  if ( move_down == 0) {
                    move_down = 1;

                    /* keep this directory name for later */
                    strcpy( next_dir_name, name_buffer);
                  }
                }
                else {
                  /* the delete failed for some other reason, oh dear */
                  success = 0;
                  complete = 1;
                }
              }
              else {
                /* failed to unlink a file, not alot can happen now */
                success = 0;
                complete = 1;
              }
            }
          }
        }
        else {
          /* completed the directory walk */
          break;
        }
      }
      dirclose( dp);

      if ( !complete) {
        if (move_down) {
          /*
           * Move into the lower directory
           */
          if ( chdir( next_dir_name) != 0) {
            success = 0;
            complete = 1;
          }
          depth += 1;
        }
        else {
          /* successfully deleted the contents of this directory
           * so unlink it */
          chdir("..");

          if ( depth == 0) {
            if ( unlink(aDirPath) != 0) {
              /* should have worked */
              success = 0;
            }
            complete = 1;
          }
          else {
            depth -= 1;
          }
        }
      }
    }
    else {
      /* time to cry */
      success = 0;
      complete = 1;
    }
  }

  chdir( orig_working_directory);

  return success;
}

#151705 - darkchild - Mon Mar 03, 2008 8:02 pm

Hmm, so recursive sometimes is bad...

And yes xD I did come from another language (VB6)

I'm still learning C to be honest :X

Still a newbie >_>

But thank you all for the help, I will try masscat's code, if it works, I'll credit ya, and of course, all of you for the great amount of help you guys gave me.

Thank you, it's good to know you guys actually care :)

Edit : masscat, the compiler gives me an error :

Code:
c:/iFile/source/includes/io.c: In function 'deleteDirTree':
c:/iFile/source/includes/io.c:215: error: 'errno' undeclared (first use in this function)
c:/iFile/source/includes/io.c:215: error: (Each undeclared identifier is reported only once
c:/iFile/source/includes/io.c:215: error: for each function it appears in.)
c:/iFile/source/includes/io.c:215: error: 'EPERM' undeclared (first use in this function)


I don't know how to work with this "Errno" -.-


Edit : Nevermind, googled a bit and found that I had to add "Errno.h" :)