Add Book to My BookshelfPurchase This Book Online

Chapter 5 - Files and Directories

UNIX Systems Programming for SVR4
David A. Curry
 Copyright © 1996 O'Reilly & Associates, Inc.

Working with Directories
Up to this point, this chapter discussed how to manipulate files and directories from one place in the filesystem, the current working directory. However, it is often necessary for systems programs to be able to work with the entire filesystem hierarchy, traversing up and down directory trees. This section describes the tools needed to do this.
Determining the Current Working Directory
Each running program has an attribute associated with it called the current working directory. This is the pathname of the directory in which the program exists. When the program specifies a relative pathname for a file, the name is taken relative to the current working directory. For example, if a program's current working directory is /one/two/three and it creates a file called foo, the full pathname to the file is /one/two/three/foo.
To determine a program's current working directory, use the getcwd function:
    #include <unistd.h>
    char *getcwd(char *buf, size_t size);
When called, getcwd determines the absolute pathname of the current working directory and places it into the character string pointed to by buf, whose size is given by size. If buf is the null pointer, getcwd allocates a string with malloc (see Chapter 2, Utility Routines), copies the pathname to it, and returns a pointer to the allocated string. If buf is not large enough or some other error occurs, getcwd returns the predefined constant NULL.
Porting notes
BSD variants provide a slightly different function, called getwd, instead of getcwd:
    #include <sys/param.h>
    char *getwd(char *path);
The pathname of the current directory is placed into path, which should be of length MAXPATHLEN. If an error occurs, an error message is placed in path and getwd returns a null pointer; otherwise, path is returned.
Changing the Current Working Directory
Two functions are provided for changing the current working directory:
    #include <unistd.h>
    int chdir(const char *path);
    int fchdir(int fd);
The chdir function changes the current working directory to the directory named by path, which can be either an absolute or a relative pathname. The fchdir function changes the current working directory to the directory referred to by the open file descriptor fd. Both functions return 0 on success and -1 on failure, storing the reason for failure in the external variable errno.
Reading Directories
Many programs, even simple ones like ls, need to read directories to learn their contents. Very old UNIX systems required the programmer to read the directory “manually” a record at a time, but most newer versions provide a library of functions to do this:
    #include <dirent.h>
    DIR *opendir(const char *path);
    struct dirent *readdir(DIR *dp);
    long telldir(DIR *dp);
    void seekdir(DIR *dp, long pos);
    void rewinddir(DIR *dp);
    int closedir(DIR *dp);
The opendir function opens the directory named in path for reading, and returns a directory stream pointer of type DIR *. If the directory cannot be opened, NULL is returned. The closedir function closes the directory stream referred to by dp.
The readdir function returns the next directory entry from the stream dp. The information is returned as a pointer to type struct dirent:
    struct dirent {
        ino_t           d_ino;
        off_t           d_off;
        unsigned short  d_reclen;
        char           *d_name;
    };
The d_ino field of the structure contains the i-node number of the entry, d_off contains the offset of the record in the directory file, d_reclen contains the length of the directory entry record, and d_name contains the name of the entry. When readdir encounters the end of the directory file, it returns the constant NULL.
The telldir function returns the current file offset in the directory file. The seekdir function sets the current offset to the value specified by pos. Both telldir and seekdir express the offset in bytes from the beginning of the directory file. The rewinddir function sets the current offset to zero.
Example 5-2 shows a program that behaves much like the ls -l command; it reads each directory named on the command line and displays one line for each file in the directory.
Example 5-2:  listfiles
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <dirent.h>
#include <stdio.h>
char     typeOfFile(mode_t);
char    *permOfFile(mode_t);
void     outputStatInfo(char *, char *, struct stat *);
int
main(int argc, char **argv)
{
    DIR *dp;
    char *dirname;
    struct stat st;
    struct dirent *d;
    char filename[BUFSIZ+1];
    /*
     * For each directory on the command line...
     */
    while (--argc) {
        dirname = *++argv;
        /*
         * Open the directory.
         */
        if ((dp = opendir(dirname)) == NULL) {
            perror(dirname);
            continue;
        }
        printf("%s:\n", dirname);
        /*
         * For each file in the directory...
         */
        while ((d = readdir(dp)) != NULL) {
            /*
             * Create the full file name.
             */
            sprintf(filename, "%s/%s", dirname, d->d_name);
            /*
            * Find out about it.
            */
            if (lstat(filename, &st) < 0) {
                perror(filename);
                putchar('\n');
                continue;
            }
            /*
             * Print out the information.
             */
            outputStatInfo(filename, d->d_name, &st);
            putchar('\n');
        }
        putchar('\n');
        closedir(dp);
    }
    exit(0);
}
/*
* outputStatInfo - print out the contents of the stat structure.
*/
void
outputStatInfo(char *pathname, char *filename, struct stat *st)
{
    int n;
    char slink[BUFSIZ+1];
    /*
     * Print the number of file system blocks, permission bits,
     * number of links, user-id, and group-id.
     */
    printf("%5d ", st->st_blocks);
    printf("%c%s ", typeOfFile(st->st_mode), permOfFile(st->st_mode));
    printf("%3d ", st->st_nlink);
    printf("%5d/%-5d ", st->st_uid, st->st_gid);
    /*
     * If the file is not a device, print its size; otherwise
     * print its major and minor device numbers.
     */
    if (((st->st_mode & S_IFMT) != S_IFCHR) &&
        ((st->st_mode & S_IFMT) != S_IFBLK))
        printf("%9d ", st->st_size);
    else
        printf("%4d,%4d ", major(st->st_rdev), minor(st->st_rdev));
    /*
     * Print the access time.  The ctime() function is
     * described in Chapter 7, "Time of Day Operations."
     */
    printf("%.12s ", ctime(&st->st_mtime) + 4);
    /*
     * Print the file name.  If it's a symblic link, also print
     * what it points to.
     */
    printf("%s", filename);
    if ((st->st_mode & S_IFMT) == S_IFLNK) {
        if ((n = readlink(pathname, slink, sizeof(slink))) < 0)
            printf(" -> ???");
        else
            printf(" -> %.*s", n, slink);
    }
}
/*
* typeOfFile - return the english description of the file type.
*/
char
typeOfFile(mode_t mode)
{
    switch (mode & S_IFMT) {
    case S_IFREG:
        return('-');
    case S_IFDIR:
        return('d');
    case S_IFCHR:
        return('c');
    case S_IFBLK:
        return('b');
    case S_IFLNK:
        return('l');
    case S_IFIFO:
        return('p');
    case S_IFSOCK:
        return('s');
    }
    return('?');
}
/*
* permOfFile - return the file permissions in an "ls"-like string.
*/
char *
permOfFile(mode_t mode)
{
    int i;
    char *p;
    static char perms[10];
    p = perms;
    strcpy(perms, "---------");
    /*
     * The permission bits are three sets of three
     * bits: user read/write/exec, group read/write/exec,
     * other read/write/exec.  We deal with each set
     * of three bits in one pass through the loop.
     */
    for (i=0; i < 3; i++) {
        if (mode & (S_IREAD >> i*3))
            *p = 'r';
        p++;
        if (mode & (S_IWRITE >> i*3))
            *p = 'w';
        p++;
        if (mode & (S_IEXEC >> i*3))
            *p = 'x';
        p++;
    }
    /*
     * Put special codes in for set-user-id, set-group-id,
     * and the sticky bit.  (This part is incomplete; "ls"
     * uses some other letters as well for cases such as
     * set-user-id bit without execute bit, and so forth.)
     */
    if ((mode & S_ISUID) != 0)
        perms[2] = 's';
    if ((mode & S_ISGID) != 0)
        perms[5] = 's';
    if ((mode & S_ISVTX) != 0)
        perms[8] = 't';
    return(perms);
}
    % lsfiles /home/msw/a
    /home/msw/a:
        2 drwxr-sr-x   7     0/1           512 Dec 21 22:20 .
        2 drwxr-xr-x   3     0/0           512 Dec 21 20:45 ..
       16 drwx------   2     0/0          8192 Apr 19 16:04 lost+found
        2 drwxr-sr-x  12    40/1          1024 Mar 12 10:16 davy
        2 drwxr-sr-x   2    43/1           512 Apr 19 17:57 sean
        2 drwxr-sr-x   3    42/1           512 Jan 12 19:59 trevor
        2 drwxr-sr-x   6    41/1           512 Feb 22 13:34 cathy
Porting notes
On BSD systems, the include file for the directory routines is called sys/dir.h instead of dirent.h, and the directory structure is of type struct direct instead of type struct dirent.
BSD systems provide two other functions as part of the directory library that didn't make it into the POSIX standard:
    #include <sys/types.h>
    #include <sys/dir.h>
    int scandir(char *dirname, struct direct *(*namelist[]),
            int (*select)(), int (*compare)());
    int alphasort(struct direct *d1, struct direct *d2);
The scandir function reads the entire contents of the directory dirname into a dynamically allocated array of structures pointed to by namelist. For each entry, it calls the user-defined select function with the name of the entry. select returns non-zero if the entry is of interest and zero if it is not. The entire namelist is sorted according to the comparison routine compare, which is passed pointers to two directory entries. It returns less than, equal to, or greater than zero depending on whether the first argument is considered less than, equal to, or greater than the second argument in the sort. You can use the alphasort function for this purpose if alphabetical order is desired.
There are public domain implementations of the directory library routines for use on very old UNIX systems that do not provide them. For portability reasons, these implementations are preferred over doing things “the hard way.”

Previous SectionNext Section
Books24x7.com, Inc © 2000 –  Feedback