Scanning a Directory

While the family of routines (see directory(3)) starting with opendir(3) performs the functions that a programmer might need, they are somewhat tedious to code if you need them frequently enough. The scandir(3) and alphasort(3) routines assist in reducing the programmer effort required:

#include <sys/types.h>
#include <dirent.h>

int scandir(
    const char *dirname,
    struct dirent ***namelist,
    int (*select)(struct dirent *),
    int (*compar)(const void *, const void *));

int alphasort(const void *d1, const void *d2);

Function scandir(3) might look somewhat intimidating. However, once you spend a moment examining it, you will see that it is easy to use. The argument dirname is given the pathname of the directory that you want to scan. The argument namelist points to a (struct dirent **) pointer, so that a list of directory entries can be returned. The argument select can be left null, if you want to select all directory names. When the argument compar is given a null pointer, the directory entries returned are unsorted.

Upon a successful return, scandir(3) returns the number of entries that are returned in the namelist array (this may include the value 0). The value -1 is returned when there is an error (no errno values appear to be formally documented).

The function alphasort(3) is a function that can be supplied in the argument compar if you require that namelist be sorted alphabetically.

Note

The namelist array is dynamically allocated and must be freed when your program no longer requires it. You must first call free(3) for each entry in the array and then free the array itself by calling free(3).


Declaring Your Own select Function for scandir(3)

The function pointer supplied for the select argument is called with one pointer to a dirent structure. Based on this, the function must return non-zero (true) if the entry is to be included (selected) in the final list of entries. If zero (false) is returned by this function, the entry is to be excluded. The following shows an example function that selects only the entries starting with h.

/*
 * Select only those directory entries that start with 'h'
 */
int
my_select(struct dirent *dp) {

    if ( dp->d_name[0] != 'h')
        return 0;                       /* Don't include this */
    return 1;                           /* else include this one */
}

The function my_select() will be called for each directory entry found by scandir(3). When my_select() returns zero, the directory entry is excluded from the final list.

Declaring Your Own compar Function for scandir(3)

The function supplied for compar is called with two void pointer arguments. The IBM AIX and FreeBSD platforms define their arguments this way. See the next section for platforms that declare these arguments differently.

The man(1) page provided by FreeBSD is not abundantly clear how you should interpret these void pointer arguments. The void pointers are actually pointers to a pointer to a dirent structure. The following example illustrates in code how they should be cast and used:

int
my_compar(const void *d1,const void *d2) {
    struct dirent *dir1 = *(struct dirent **)d1;
    struct dirent *dir2 = *(struct dirent **)d2;

    return strcmp(dir1->d_name,dir2->d_name);
}

The code shown implements what the function alphasort(3) provides. The two void pointers are cast to a (struct dirent **) and then dereferenced once to point to the struct dirent entry itself. Once this is done, then strcmp(3) can be called upon to provide a comparison result to be returned.

SysV Variations

You will find that some systems will declare the compar and alphasort(3) functions differently. These systems use the following synopsis:

#include <sys/types.h>
#include <dirent.h>

/* SysV Definiton : */

int scandir(const char *dirname,
    struct dirent **namelist[],
    int (*select)(struct dirent *),
    int (*compar)(struct dirent **, struct dirent **));

int alphasort(struct dirent **d1, struct dirent **d2);

The notable difference here is that the compar function pointer is defined in terms of a function that receives pointers to (struct dirent **) instead of (void *). In this case, you would define the function my_compar() in the following manner:

int
my_compar(struct dirent **d1,struct dirent **d2) {
    struct dirent *dir1 = *d1;
    struct dirent *dir2 = *d2;

    return strcmp(dir1->d_name,dir2->d_name);
}

Platforms that use this definition include SGI's IRIX 6.5, UnixWare-7, Sun's Solaris 8, and HPUX 11.

A scandir(3) Example

An example program making use of the scandir(3) function is provided in Listing 7.2.

Code Listing 7.2. scandir.c—A Demonstration Program Using scandir(3)
1:   #include <stdio.h>
2:   #include <stdlib.h>
3:   #include <unistd.h>
4:   #include <errno.h>
5:   #include <sys/types.h>
6:   #include <dirent.h>
7:  
8:   extern int scandir(const char *dirname, struct dirent ***namelist,
9:       int (*select)(struct dirent *),
10:      int (*compar)(const void *, const void *));
11: 
12:  extern int alphasort(const void *d1, const void *d2);
13: 
14:  /*
15:   * Select only those directory entries that start with
16:   * 'h'to demonstrate the selection ability :
17:   */
18:  static int
19:  my_select(struct dirent *dp) {
20: 
21:      if ( dp->d_name[0] != 'h')
22:          return 0;                       /* Don't include this */
23:      return 1;                           /* else include this one */
24:  }
25: 
26:  /*
27:   * Sort entries in reverse order for demonstration
28:   * purposes :
29:   */
30:  static int
31:  my_compar(const void *d1,const void *d2) {
32:      struct dirent *dir1 = *(struct dirent **)d1;
33:      struct dirent *dir2 = *(struct dirent **)d2;
34: 
35:      /*
36:       * Reverse the comparison by reversing
37:       * dir2 with dir1 in the strcmp(3) call:
38:       */
39:      return strcmp(dir2->d_name,dir1->d_name);
40:  }
41: 
42:  /*
43:   * A good test is the directory /etc :
44:   */
45:  int
46:  main(int argc,char **argv) {
47:      int x;                              /* Work index */
48:      int n;                              /* namelist[n] */
49:      struct dirent **namelist;           /* List of names */
50: 
51:      if ( argc < 2 ) {
52:          fputs("A pathname argument is required.
"
53:              "Try /etc for the directory.
",stderr);
54:          return 1;
55:      }
56:     
57:      /*
58:       * Scan the directory given :
59:       */
60:      n = scandir(argv[1],&namelist,my_select,my_compar);
61: 
62:      /*
63:       * Report the directory entries :
64:       */
65:      printf("%d entries for %s:
",n,argv[1]);
66:      for ( x=0; x<n; ++x )
67:          printf("%3d: %s
",x,namelist[x]->d_name);
68: 
69:      if ( n > 0 ) {
70:          for ( x=0; x<n; ++x )
71:              free(namelist[x]);          /* Release entry */
72:          free(namelist);                 /* Release the array */
73:      }
74:      return 0;
75:  }

The main program shown in Listing 7.2 is straightforward. The scandir(3) function is called on line 60, using argv[1] as the directory that is to be scanned. The list of directory entries will be returned to the pointer namelist, which is declared in line 49. The number of entries returned by scandir(3) is stored to variable n, which is declared in line 48.

You have seen the function my_select() before, for example on page 140. The function my_compar() was altered slightly from the example shown on page 141 to sort the entries in reverse order (lines 30–40).

Finally, notice how the allocated storage is released in lines 69–73 of the main() program. First all of the array elements are released (line 71), and then the array itself (line 72).

Compiling and running the program yields the following results:

$ make scandir
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall scandir.c
cc scandir.o -o scandir
$ ./scandir /etc
5 entries for /etc:
  0: hosts.lpd
  1: hosts.equiv
  2: hosts.allow
  3: hosts
  4: host.conf
$

Using the directory /etc, you can see that, indeed, only the filenames starting with h were selected. Thanks to the custom sort function my_compar(), the entries were sorted in reverse alphabetical order as well.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.71.146