profile for David C. Rankin at Stack Overflow, Q&A for professional and enthusiast programmers

C References


Tutorials

Of all the online tutorials, these tutorials are among the best for the areas they discuss.


Libraries

These libraries will make your life easier. They are complete with Makefiles and test programs showing the library use. Just untar the files into a test directory and issue 'make'.


Code Tools

The tools below are general utilities for code and web development.


Comments/Bugs

If you have a comment or suggestion or find a bug in the code, please feel free to send a report.

C scandir Example with Extension Filter

scandir Usage

Continuing from the opendir/readir example, here is an implementation with scandir that reads each entry in the directory given as the first argument (or “.” by default) with a .c, .h extnsion and uses stat to obtain the file size and a helper to count the lines in each file. scandir being a wrapper to opendir/readdir, will essentially open the directory and repeatedly call readdir allocating space to read all entries into an array of struct dirent (technically a pointer-to-pointer-to-struct-dirent). Since the memory is dynamically allocated, you are responsible for freeing the list when it is no longer needed.

Sample Code:

#define _SVID_SOURCE    /* scandir */

#include <stdio.h>      /* printf */
#include <stdlib.h>     /* free   */
#include <string.h>     /* strlen, strrchr */

#include <dirent.h>     /* scandir, struct dirent */

#include <sys/types.h>  /* stat, struct stat */
#include <sys/stat.h>
#include <unistd.h>

int filter (const struct dirent *d);
size_t nlines (char *fn);
char *stripfwd (char *fn);
char *addpath (char *ffn, const char *path, const char *fn);

int main (int argc, char **argv) {

    int i, n;
    char *dir = argc > 1 ? argv[1] : ".";
    struct dirent **namelist;

    stripfwd (dir);     /* tidy up dir, remove any trailing '/' */

    if ((n = scandir (dir, &namelist, filter, alphasort)) == -1) {
        fprintf (stderr, "error: scandir failed for '%s'.\n", dir);
        return 1;
    }

    for (i = 0; i < n; i++) {               /* for each entry */
        struct stat st;
        char fn[FILENAME_MAX] = "";

        /* add path before filename */
        addpath (fn, dir, namelist[i]->d_name);

        if (stat (fn, &st) == -1)  /* stat filename to get size (bytes) */
            fprintf (stderr, "error: stat failed for '%s'\n", fn);
        else {
            size_t lines = nlines (fn);   /* get lines in file */
            printf ("%-32s  %9lu  %zu\n", fn, st.st_size, lines);
        }
    }

    for (i = 0; i < n; i++)
        free (namelist[i]);     /* free each entry */
    free (namelist);            /* free pointers  */

    return 0;
}

/** filter function to select only files with '.c' and '.h'
 *  file extensions
 */
int filter (const struct dirent *d)
{
    if (!d) return 0;                       /* validate struct ptr  */

    size_t len = strlen (d->d_name);        /* lengh of filename    */
    char *p = strrchr (d->d_name, '.');     /* position of last '.' */

    if (!p || len < 3) return 0;     /* no '.' or len < 3 no match  */

    if ((size_t)(p - d->d_name) == len - 2 &&   /* next to last '.' */
        (*(p + 1) == 'c' || *(p + 1) == 'h'))   /* last 'c' or 'h'  */
        return 1;

    return 0;
}

/** open and read each line in 'fn' returning the number of lines */
size_t nlines (char *fn)
{
    if (!fn) return 0;

    size_t lines = 0, n = 0;
    char *buf = NULL;
    FILE *fp = fopen (fn, "r");

    if (!fp) return 0;

    while (getline (&buf, &n, fp) != -1)  lines++;

    fclose (fp);
    free (buf);

    return lines;
}

/** remove '/' at end of 'fn' */
char *stripfwd (char *fn)
{
    size_t len = strlen (fn);

    while (len && fn[len - 1] == '/')
        fn[--len] = 0;

    return fn;
}

/** add 'path' component to beginning of 'fn', return 'ffn' */
char *addpath (char *ffn, const char *path, const char *fn)
{
    if (!ffn || !path || !fn) return NULL;

    if (strcmp (path, ".")) {  /* if path isn't ".", add path to fn */
        strcpy (ffn, path);
        strcat (ffn, "/");
        strcat (ffn, fn);
    }
    else
        strcpy (ffn, fn);

    return ffn;
}

Compile:

gcc -Wall -Wextra -finline-functions -O3 -o scandir_ex scandir_ex.c

Example Input:

$ ls -l tmp
drwxr-xr-x 5 david david  4096 Jul 20  2015 .
drwxr-xr-x 7 david david  4096 Jun 17 02:14 ..
drwxr-xr-x 2 david david  4096 Jul  9  2014 bin
drwxr-xr-x 5 david david  4096 Jul  8  2014 d1
drwxr-xr-x 5 david david  4096 Jul  8  2014 d2
-rw-r--r-- 1 david david  4493 May 10  2014 rdrmstat.c
-rw-r--r-- 1 david david     0 Jul 15  2015 rdrmstat.h
-rw-r--r-- 1 david david 96293 May 10  2014 rmftw-io-out.txt
lrwxrwxrwx 1 david david    22 Jul 15  2015 test-isdir-access.c -> ../test-isdir-access.c
lrwxrwxrwx 1 david david    12 Jul 15  2015 tstfile.c -> ../tstfile.c
-rw-r--r-- 1 david david  2580 Jul 20  2015 walk-ftw-test.c
-rw-r--r-- 1 david david  1527 Jul  9  2014 walk-nftw-test.c

Use/Output:

$ ./scandir_ex tmp
tmp/rdrmstat.c                         4493  144
tmp/rdrmstat.h                            0  0
tmp/test-isdir-access.c                2234  90
tmp/tstfile.c                          4324  167
tmp/walk-ftw-test.c                    2580  99
tmp/walk-nftw-test.c                   1527  66

Memory Error and Leak Check:

In any code your write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.

For Linux valgrind is the normal choice. There are many subtle ways to misuse a pointer or new block of memory. Using a memory error checker allows you to identify any problems and validate proper use of of the memory you allocate rather than finding a problem exists through a segfault. There are similar memory checkers for every platform. They are all simple to use, just run your program through it. The output of valgrind is easy to interpret, e.g.:

$ valgrind ./yourprogram arguments
==11777== Memcheck, a memory error detector
==11777== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==11777== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==11777== Command: /full/path/to/yourprogram arguments
==11777==

  your program output and any errors are listed here.

==11777==
==11777== HEAP SUMMARY:
==11777==     in use at exit: 0 bytes in 0 blocks
==11777==   total heap usage: 7 allocs, 7 frees, 35,240 bytes allocated
==11777==
==11777== All heap blocks were freed -- no leaks are possible
==11777==
==11777== For counts of detected and suppressed errors, rerun with: -v
==11777== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

Always confirm under HEAP SUMMARY that All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.

Developed in KDE3:

Quanta+ from KDE3 KDE3 now developed as Trinity Desktop