Pulling a Directory

O how well I remember DOS. It wasn’t Unix, but it cribbed from Unix heavily. One of the popular DOS commands was DIR, which was like the shell command ls, but more primitive. A typical DOS directory listing looked something like this:

 Volume in drive C is TOO LOUD
 Directory of C:\PROG\C
.            <DIR>
..           <DIR>
0901a    c        383   8-18-18   9:42p
0901b    c        506   8-19-18  12:13p
0908     c        796   8-25-18  11:24a
0915     c         44   8-04-18   3:27p
a      out       8940   8-25-18  11:24a
gettys~1 txt      177   9-05-15   9:59a
sto          <DIR>
        6 File(s)   bytes free

The filename came first, split into the 8-character name and 3-character extension. Then came the file’s size, or <DIR> for a directory, followed by the date and time stamp. The cmd shell in Windows displays the filename last because the original 8-dot-3 format was discarded long ago; the original format is shown above.

To imitate this format in C, you must extract each filename in the directory, its size, timestamp, and determine whether the file is a directory. From last week’s Lesson, I demonstrated how to use the stat() function and S_ISDIR macro to pull out directory entries. The two other tidbits required for a DOS-like directory listing are the file’s size and its timestamp. Both of these items can be pulled from structure returned from the stat() function.

A file’s size is located in the stat structure’s st_size member.

The stat structure has three members that deal with timestamps:

st_atime, which returns when file data was last accessed
st_mtime, which returns when the file’s data was last modified
st_ctime, which returns when the file’s status was changed

A fourth member, st_birthtime, returns when the file was created, though this member isn’t available to all operating systems.

You can read more about these fields from my March 24 2018 Lesson here.

The following code represents my attempt to ape the old DOS directory command output. I didn’t bother splitting the filenames between name and extension or cropping long filenames. And I don’t use the exact format for the timestamp as it would be a bother to extract.

#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>

int main()
{
    DIR *folder;
    struct dirent *entry;
    struct stat filestat;
    char cwdpath[256];
    int count = 1;
    long long total = 0;

    folder = opendir(".");
    if(folder == NULL)
    {
        perror("Unable to read directory");
        return(1);
    }

    /* State directory name */
    getcwd(cwdpath,256);
    printf(" Directory of %s\n\n",cwdpath);

    /* Read directory entries */
    while( (entry=readdir(folder)) )
    {
        /* Extract Filename */
        stat(entry->d_name,&filestat);
        printf("%-16s",entry->d_name);

        /* Extract Size */
        if( S_ISDIR(filestat.st_mode) )
            printf("%-8s  ","<DIR>");
        else
        {
            printf("%8lld  ",filestat.st_size);
            total+=filestat.st_size;
        }

        /* Extract create date and time */
        printf("%s",ctime(&filestat.st_mtime));
    }

    closedir(folder);
    printf("\n        %d File(s) for %lld bytes\n",
            count,
            total
          );

    return(0);
}

For each directory entry read, the stat() function fills the filestat structure. The file’s full name is displayed at Line 32. I used a left-justified string output buffer with the placeholder %-16s. The 16-character width might be too narrow for some longer filenames.

The filestat.st_mode member and S_ISDIR macro help pull out directories. Unlike last week’s example, for a regular file, the filestat.st_size member helps read the file’s size in bytes. This member is typecast as a off_t variable, which translates into a long long integer and uses the %lld placeholder.

The date and time stamp is pulled at Line 44, which is similar to examples in my blog post cited previously in this Lesson.

Here’s sample output:

 Directory of /Users/dang/prog/c/blog

.               <DIR>     Sat Aug 25 12:07:13 2018
..              <DIR>     Sun Aug  5 19:40:28 2018
0901a.c              383  Sat Aug 18 21:42:04 2018
0901b.c              506  Sun Aug 19 12:13:32 2018
0908.c               954  Sat Aug 25 12:07:08 2018
0915.c                44  Sat Aug  4 15:27:24 2018
a.out               8940  Sat Aug 25 12:07:13 2018
gettysburg.txt       177  Sat Sep  5 08:59:18 2015
sto             <DIR>     Sat Feb 25 08:06:17 2017

         9 File(s) for 11004 bytes

In next week’s Lesson, I explore recursively spelunking a directory structure.

Leave a Reply