#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;
}