Topic : Files and Folders
Author : LUPG
Page : << Previous 6  Next >>
Go to page :


code demonstrates combining many system calls together to achieve a task. The source code for this program is found in the file rename-log.c.




Reading The Contents Of Directories
After we have learned how to write the contents of a file, we might wish to know how to read the contents of a directory. We could open the directory and read its contents directly, but this is not portable. Instead, we have a standard interface for opening a directory and scanning its contents, entry by entry.




The DIR And dirent Structures
When we want to read the contents of a directory, we have a function that opens a directory, and returns a DIR structure. This structure contains information used by other calls to read the contents of the directory, and thus this structure is for directory reading, what the FILE structure is for files reading.

When we use the DIR structure to read the contents of a directory, entry by entry, the data regarding a given entry is returned in a dirent structure. The only relevant field in this structure is d_name, which is a null-terminated character array, containing the name of the entry (be it a file or a directory). note - the name, NOT the path.




Opening And Closing A Directory
In order to read the contents of a directory, we first open it, using the opendir() function. We supply the path to the directory, and get a pointer to a DIR structure in return (or NULL on failure). Here is how:


#include <dirent.h>    /* struct DIR, struct dirent, opendir().. */

/* open the directory "/home/users" for reading. */
DIR* dir = opendir("/home/users");
if (!dir) {
    perror("opendir");
    exit(1);
}



When we are done reading from a directory, we can close it using the closedir() function:

if (closedir(dir) == -1) {
    perror("closedir");
    exit(1);
}


closedir() will return '0' on success, or '-1' if it failed. Unless we have done something really silly, failures shouldn't happen, as we never write to a directory using the DIR structure.




Reading The Contents Of A Directory
After we opened the directory, we can start scanning it, entry by entry, using the readdir() function. The first call returns the first entry of the directory. Each successive call returns the next entry in the directory. When all entries have been read, NULL is returned. Here is how it is used:


/* this structure is used for storing the name of each entry in turn. */
struct dirent* entry;

/* read the directory's contents, print out the name of each entry.   */
printf("Directory contents:\n");
while ( (entry = readdir(dir)) != NULL) {
    printf("%s\n", entry->d_name);
}


If you try this out, you'll note that the directory always contains the entries "." and "..", as explained in the beginning of this tutorial. A common mistake is to forget checking these entries specifically, in recursive traversals of the file system. If these entries are being traversed blindingly, an endless loop might occur.

Note: if we alter the contents of the directory during its traversal, the traversal might skip directory entries. Thus, if you intend to create a file in the directory, you would better not do that while in the middle of a traversal.




Rewinding A Directory For A Second Scan
After we are done reading the contents of a directory, we can rewind it for a second pass, using the rewinddir() function:

rewinddir(dir);




Checking And Changing The Working Directory
Sometimes we wish to find out the current working directory of a process. The getcwd() function is used for that. Other times we wish to change the working directory of our process. This will allow using short paths when accessing several files in the same directory. The chdir() system call is used for this. Here is an example:

/* this buffer is used to store the full path of the current */
/* working directory.                                        */
#define MAX_DIR_PATH 2048;
char cwd[MAX_DIR_PATH+1];

/* store the current working directory.    */
if (!getcwd(cwd, MAX_DIR_PATH+1)) {
    perror("getcwd");
    exit(1);
}

/* change the current directory to "/tmp". */
if (!chdir("/tmp")) {
    perror("chdir (1)");
    exit(1);
}

/* restore the original working directory. */
if (chdir(cwd) == -1) {
    perror("chdir (2)");
    exit(1);
}





A Complete Example
As an example, we will write a limited version of the Unix 'find' command. This command basically accepts a file name and a directory, and finds all files under that directory (or any of its sub-directories) with the given file name. The original program has zillions of command line options, and can also handle file name patterns. Our version will only be able to handle substrings (that is, finding the files whose names contain the given string). The program changes its working directory to the given directory, reads its contents, and recursively scans each sub-directory it encounters. The program does not traverse across symbolic-links to avoid possible loops. The complete source code for the the program is found in the find-file.c file.



Code examples from the tutorial:

find-file.c


/*
* find-file.c - find all files residing in the given sub-directory,
* whose names contain the given string.
*/

#include <stdio.h>              /* standard input/output routines.    */
#include <dirent.h>             /* readdir(), etc.                    */
#include <sys/stat.h>           /* stat(), etc.                       */
#include <string.h>             /* strstr(), etc.                     */
#include <unistd.h>             /* getcwd(), etc.                     */

#define MAX_DIR_PATH 2048 /* maximal full path we support.      */

/*
* function: findfile. recusively traverse the current directory, searching
*                     for files with a given string in their name.
* input:    string to match.
* output:   any file found, printed to the screen with a full path.
*/
void
findfile(char* pattern)
{
    DIR* dir;   /* pointer to the scanned directory. */
    struct dirent* entry; /* pointer to one directory entry.   */
    char cwd[MAX_DIR_PATH+1]; /* current working directory.        */
    struct stat dir_stat;       /* used by stat().                   */

    /* first, save path of current working directory */
    if (!getcwd(cwd, MAX_DIR_PATH+1)) {
 perror("getcwd:");
 return;
    }


    /* open the directory for reading */
    dir = opendir(".");
    if (!dir) {
 fprintf(stderr, "Cannot read directory '%s': ", cwd);
 perror("");
 return;
    }

    /* scan the directory, traversing each sub-directory, and */
    /* matching the pattern for each file name.               */
    while ((entry = readdir(dir))) {
 /* check if the pattern matchs. */
 if (entry->d_name && strstr(entry->d_name, pattern)) {
     printf("%s/%s\n", cwd, entry->d_name);
 }
        /* check if the given entry is a directory. */
        if (stat(entry->d_name, &dir_stat) == -1) {
     perror("stat:");
     continue;
        }
 /* skip the "." and ".." entries, to avoid loops. */
 if (strcmp(entry->d_name, ".") == 0)
     continue;
 if (strcmp(entry->d_name, "..") == 0)
     continue;
 /* is this a directory? */
        if (S_ISDIR(dir_stat.st_mode)) {
            /* Change into the new directory */
            if (chdir(entry->d_name) == -1) {
         fprintf(stderr, "Cannot chdir into '%s': ", entry->d_name);
         perror("");
       


Page : << Previous 6  Next >>