GNU/Linux provides functions for reading the contents of directories. Although these aren't directly related to the low-level I/O functions described in this appendix, we present them here anyway because they're often useful in application programs.
To read the contents of a directory, follow these steps:
1. Call opendir, passing the path of the directory that you want to examine. The call to opendir returns a DIR* handle, which you'll use to access the directory contents. If an error occurs, the call returns NULL.
2. Call readdir repeatedly, passing the DIR* handle that you obtained from opendir. Each time you call readdir, it returns a pointer to a struct dirent instance corresponding to the next directory entry. When you reach the end of the directory's contents, readdir returns NULL.
The struct dirent that you get back from readdir has a field d_name, which contains the name of the directory entry.
3. Cal closedir, passing the DIR* handle, to end the directory listing operation.
Include <sys/types.h> and <dirent.h> if you use these functions in your program.
Note that if you need the contents of the directory arranged in a particular order, you'll have to sort them yourself.
The program in Listing B.8 prints out the contents of a directory. The directory may be specified on the command line, but if it is not specified, the program uses the current working directory. For each entry in the directory, it displays the type of the entry and its path. The get_file_type function uses lstat to determine the type of a file system entry.
#include <assert.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/* Return a string that describes the type of the file system entry PATH. */
const char* get_file_type (const char* path)
{
struct stat st;
lstat (path, &st);
if (S_ISLNK (st.st_mode))
return "symbolic link";
else if (S_ISDIR (st.st_mode))
return "directory";
else if (S_ISCHR (st.st_mode))
return "character device";
else if (S_ISBLK (st.st_mode))
return "block device";
else if (S_ISFIFO (st.st_mode))
return "fifo";
else if (S_ISSOCK (st.st_mode))
return "socket";
else if (S_ISREG (st.st_mode))
return "regular file";
else
/* Unexpected. Each entry should be one of the types above. */
assert (0);
}
int main (int argc, char* argv[])
{
char* dir_path;
DIR* dir;
struct dirent* entry;
char entry_path[PATH_MAX + 1];
size_t path_len;
if (argc >= 2)
/* If a directory was specified on the command line, use it. */
dir_path = argv[1];
else
/* Otherwise, use the current directory. */
dir_path = ".";
/* Copy the directory path into entry_path. */
strncpy (entry_path, dir_path, sizeof (entry_path));
path_len = strlen (dir_path);
/* If the directory path doesn't end with a slash, append a slash. */
if (entry_path[path_len - 1] != '/') {
entry_path[path_len] = '/';
entry_path[path_len + 1] = '\0';
++path_len;
}
/* Start the listing operation of the directory specified on the
command line. */
dir = opendir (dir_path);
/* Loop over all directory entries. */
while ((entry = readdir (dir)) != NULL) {
const char* type;
/* Build the path to the directory entry by appending the entry
name to the path name. */
strncpy (entry_path + path_len, entry->d_name,
sizeof (entry_path) - path_len);
/* Determine the type of the entry. */
type = get_file_type (entry_path);
/* Print the type and path of the entry. */
printf ("%-18s: %s\n", type, entry_path);
}
/* All done. */
closedir (dir);
return 0;
}
Here are the first few lines of output from listing the /dev directory. (Your output might differ somewhat.)
% ./listdir /dev
directory : /dev/.
directory : /dev/..
socket : /dev/log
character device : /dev/null
regular file : /dev/MAKEDEV
fifo : /dev/initctl
character device : /dev/agpgart
...
To verify this, you can use the ls command on the same directory. Specify the -U flag to instruct ls not to sort the entries, and specify the -a flag to cause the current directory ( . ) and the parent directory ( .. ) to be included.
% ls -lUa /dev
total 124
drwxr-xr-x 7 root root 36864 Feb 1 15:14 .
drwxr-xr-x 22 root root 4096 Oct 11 16:39 ..
srw-rw-rw- 1 root root 0 Dec 18 01:31 log
crw-rw-rw- 1 root root 1, 3 May 5 1998 null
-rwxr-xr-x 1 root root 26689 Mar 2 2000 MAKEDEV
prw------- 1 root root 0 Dec 11 18:37 initctl
crw-rw-r-- 1 root root 10, 175 Feb 3 2000 agpgart
...
The first character of each line in the output of ls indicates the type of the entry.