2013-03-26 48 views
0
  • 平臺:Windows XP服務包3
  • 編譯:代碼:: Blocks的版本12.11

我目前正在寫一個程序,將遞歸刪除給定的目錄使用POSIX目錄功能。但是,我對readdir()和它的對應dirent結構有問題。我在readdir的documentation中讀到,對函數的多次調用將覆蓋函數返回的結構中保存的數據。所以我認爲readdir()必須爲結構本身包含內存,然後簡單地將指針地址重新分配給捕獲返回值的結構。我測試了這個理論,並且我是正確的readdir()爲它的成員d_name分配了內存。我遇到的問題是,當目錄流爲空時,readdir返回一個空指針,所以我使用while循環(條件爲dirent_ptr!= NULL)迭代整個目錄。但是因爲readdir()將處理結構的內存分配,我只需聲明一個dirent結構並讓readdir()完成它的工作。由於某種原因,dirent結構被初始化爲NULL(或者它可能是我的編譯器),所以我的循環從不啓動,因爲它的條件語句不是initialy true。所以我想我的問題是我在這裏做錯了什麼?我需要分配內存的結構的dirent

這裏是重要的變量聲明和包含的圖書館。請注意,所有這些變量都是全局聲明的。

#include <stdio.h> 
#include <stdlib.h> 
#include <dirent.h> 
#include <sys/stat.h> 
#include <string.h> 

int recursive_delete(const char *path); 
int file_delete(const char *path, int file_size); 

struct dirent *direntp; 
struct stat *statp; 

struct switches 
{ 
    int verbose; 
    int no_prompt; 
    int continue_if_error; 
    int files_only; 
}; struct switches switches; 

而不是解析相對路徑我簡單地cd到作爲參數給出的路徑,然後使用。和通配符移動導向器,以使相對路徑(d_names)有效。另外開關結構只包含命令行開關,應該被忽略,我知道在下面的代碼中的錯誤,但非常不能修復它們,因爲我無法克服上述問題。

int recursive_delete(const char *path) 
{ 
    DIR *dirp; 
    int return_value = 0; 
    int recursive_return_value = 0; 

    if((chdir(path)) == -1) 
    { 
     perror("ERROR(3)"); 
     return 1; 
    } 
    printf("CDED to \"%s\"\n", path); 

    dirp = opendir("."); 
    if(dirp == NULL) 
    { 
     perror("ERROR(4)"); 
     return 1; 
    } 
    printf("OPENED \"%s\"\n", path); 

    while(direntp != NULL) 
    { 
     direntp = readdir(dirp); 
     if((direntp == NULL) && (errno != 0)) 
     { 
      perror("ERROR(5)"); 
      return 1; 
     } 
     printf("READ \"%s\" FROM \"%s\"\n", direntp->d_name, path); 

     if((strcmp(direntp->d_name, ".")!=0) && (strcmp(direntp->d_name, "..")!=0)) 
     { 
      if((stat(direntp->d_name, statp)) == -1) 
      { 
       perror("ERROR(6)"); 
       return 1; 
      } 
      printf("STATED \"%s\"\n", direntp->d_name); 

      if(S_ISREG(statp->st_mode)) 
      { 
       printf("DELETING \"...\\%s\\%s\"\n", path, direntp->d_name); 
       return_value += file_delete(direntp->d_name, statp->st_size); 
       if((!switches.continue_if_error) && (return_value != 0)) 
       { 
        break; 
       } 
      } 
      else if(S_ISDIR(statp->st_mode)) 
      { 
       printf("\n\n\nCALLING RECURSIVE DELETE with \"%s\"\n", direntp->d_name); 
       recursive_return_value = recursive_delete(direntp->d_name); 
       return_value += recursive_return_value; 

       if((!switches.continue_if_error) && (recursive_return_value != 0)) 
       { 
        break; 
       } 

       if((!switches.files_only) && (recursive_return_value == 0)) 
       { 
        if((chdir("..")) == -1) 
        { 
         perror("ERROR(6)"); 
         return 1; 
        } 
        printf("CDED BACK TO \"%s\" FROM \"%s\"\n", path, direntp->d_name); 

        if((rmdir(direntp->d_name)) == -1) 
        { 
         perror("ERROR(7)"); 
         return 1; 
        } 

        if(switches.verbose) 
        { 
         printf("DELETED DIRECTORY \"...\\%s\\\"\n\n\n", direntp->d_name); 
        } 
       } 
      } 
     } 
    } 

    return return_value; 
} 

回答

1

你的代碼結構應該看起來像thhis(爲了清楚省略了大多數錯誤檢查):

int recursive_delete(const char *path) 
{ 
    DIR* dirp = NULL; 
    int return_value = 0; 
    char* initial_cur_dir = malloc(1000); 

    getcwd(initial_cur_dir, 1000); 
    chdir(path); 
    dirp = opendir("."); 

    while (dirp != NULL) 
    { 
    struct dirent* direntp; 
    struct stat stat; 

    direntp = readdir(dirp); 

    if (direntp == NULL) 
     break; 

    stat(direntp->d_name, &stat); 

    if (S_ISDIR(statp->st_mode)) 
    { 
     if (strcmp(direntp->d_name, ".") && strcmp(direntp->d_name, "..")) 
     { 
     return_value += recursive_delete(direntp->d_name); 
     } 
    } 
    else if (S_ISREG(statp->st_mode)) 
    { 
     unlink(direntp->d_name); 
    } 
    } 

    if (initial_cur_dir != NULL) 
    { 
    chdir(initial_cur_dir); 
    rmdir(path); 
    } 

ErrorLabel: // you should goto here when an error is detected 

    if (dirp != NULL) 
    { 
    closedir(dirp); 
    } 

    if (initial_cur_dir != NULL) 
    { 
    chdir(initial_cur_dir); 
    free(initial_cur_dir); 
    } 

    return return_value; 
} 
+0

感謝您的回覆,但我有點失落,因爲這對我有幫助。每次循環開始時聲明direntp是毫無意義的,除非我將dirent結構的實際值複製到不同的結構中,因爲readdir()不使用接收它的返回值的dirent結構的指針地址。另外我想你錯過了理解改變當前目錄的要點。如果我使用上面的代碼,那麼相對路徑是有效的,因爲recurisive_delete()將更改cwd,rmdir(direntp-> d_name)將返回一個No such file或directory error。 – 2013-03-26 22:45:38

+0

你沒看到第二個'chdir()'在最後?我傾向於使用盡可能少的全局變量來避免諸如爭用條件之類的事情,並將相關的代碼和數據緊密結合在一起。 – 2013-03-26 22:56:49

+0

第二個想法是,上面的代碼不會刪除指定的目錄,只會刪除它中的所有內容。我會更新它來解決這個問題。 – 2013-03-26 23:09:51

1

從連接的代碼,其中direntp被(前while迴路)初始化目前還不清楚。也許你可以試試:

direntp = readdir(dirp); 
while(direntp != NULL) 
{ 
    // all your work here 

    direntp = readdir(dirp); 
} 

這種模式保證了direntp初始化和更新while循環。然而,第二眼看你的代碼我並不確定while循環本來應該做什麼。 direntpdirp如何在循環中更改?

這有可能是你可以逃脫的if測試(而不是while),只是讓遞歸調用處理「循環」的效果......

+0

感謝您的答覆,並回答您的問題direntp沒有初始化它只是globaly在主函數之前的空白聲明。這是我的問題,我想知道它是如何被初始化爲NULL的。另外direntp應該保存由dirp指向的文件/目錄的文件/目錄名稱,它應該是當前工作目錄的目錄流的當前索引。 – 2013-03-26 22:55:20

+0

哦,我幾乎忘了,是的使用if語句而不是循環會工作,但這會使堆棧溢出的可能性更大,因爲該函數將爲每個單個文件和orginial參數的子目錄進行調用。recursive_delete()被設計爲當它在當前工作目錄中找到一個子目錄時,它應該只記得它是自己的。 – 2013-03-26 22:58:33

+0

@JohnVulconshinz關於'direntp'的初始化:如果你是*幸運*,你的聲明將被清零(即NULL),但通常不能保證。我在不同的平臺/環境中看到了不同的行爲。如果沒有明確的分配,最初可能會爲您的指針創建一些垃圾值。 – SeKa 2013-03-27 00:36:07