1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
#include "stdio.h"
#include "dirent.h" /* NOT PRESENT on all systems */
#include "stdlib.h"
#include "sys/stat.h"
// The key function is used recursively, and this value must be maintained across all recursions.
// Alternatively, could make it a parameter to the key function
int dirDepth; // Keep count of how deep into the directory tree we are.
char permissions[10]; // An array of ten characters that will be used to show the ten
// characteristics of interest (file/dir, read/write/execute
// permissions for owner/group/others
// listContents is a function that returns an int, intended to be used for indicating
// success or failure. It takes a pointer to a constant character (i.e. an array of
// characters that identifies a location in the filesystem - it is constant to remind
// the coder/maintainer not to mess with it, as it's of vital importance)
int listContents(const char *path) {
int i = 0; /* Loop counter used later */
struct dirent *entry; /* THIS STRUCT DIFFERS ACROSS SYSTEMS - THIS IS NOT PORTABLE CODE */
DIR* p_startingDirectory; // a pointer to an object of type DIR. Type DIR is a structure
// representing a directory stream. Defined in dirent.h
// Note that at this point, the pointer exists but does not point
// to a valid structure, as one has not been created
p_startingDirectory = opendir(path); // Creates the directory stream, returns a pointer to it.
// The structure will be tidied up later with closedir
if (p_startingDirectory == NULL) // Error check for good return from above
{
perror("opendir");
return -1; // -1 is the return code that listContents returns to indicate failure.
// perror dumps useful info to stderr
}
while((entry = readdir(p_startingDirectory))) // If readdir fails, entry will be null and
// while loop will end. Otherwise, entry will
// be a dirent struct. readdir returns the next entry
{
/* Do not list "." or ".." */
if (!(strcmp(".", entry->d_name)))
{
continue; // back to start of while loop (which causes a fresh evaluation of the while condition,
// so changes entry
}
if (!(strcmp("..", entry->d_name)))
{
continue;
}
/* Get stat details */
struct stat buf;
/* Blank existing permissions for new file or dir */
for (i=0;i<10;++i)
{
permissions[i]='-'; // Just as in ls -l
}
// Build complete pathname for this file or dir
// How much memory do we need for the new path? This much.
char* completePath = malloc(snprintf(NULL, 0, "%s/%s", path, entry->d_name) + 1);
// snprintf prints the complete path and name and a 's' to NULL. Snprintf returns the number of
// characters written.
// add one because C-style strings are array of char finishing with the value (NOT character)
// zero.
// Feed that value to malloc, get back a void* to some memory. Cast it to a char*.
// Strictly speaking, should do some kind of error checking to ensure that malloc returned
// memory
// Now create completePath, which is what it sounds like
sprintf(completePath, "%s/%s", path, entry->d_name);
stat(completePath, &buf); // Fill buf with the details of file/dir specified by completePath
// Mark each output as a file or a directory for the user
if (entry->d_type == DT_DIR)
{
permissions[0]='d';
}
else if (entry->d_type == DT_REG)
{
permissions[0]='f';
}
// Two examples showing how to get permissions. Used bitwise AND.
if (buf.st_mode & S_IRUSR)
{
permissions[1]='r';
}
if (buf.st_mode & S_IWOTH)
{
permissions[8]='w';
}
/* Indent to show user directory contents */
// Does NOT match the style of ls -R, but does make it clear to see
for (i=0; i<dirDepth; ++i)
{
printf(" ");
}
printf("%s %zd %s \n", permissions, buf.st_size, entry->d_name ); // print to stdout permissions, size, name
/* if this is a directory, go into it and start listing the contents */
/* Go go Gadget recursion */
if (entry->d_type == DT_DIR)
{
// Using dirDepth to make clear to the user which files are in which directory
++dirDepth; // So that we indent properly for subdirectories
listContents(completePath);
--dirDepth; // Now have come back out of the subdirectory, so un-indent
}
free(completePath); // Tidy up memory. If we malloced it, we must free it.
}
closedir(p_startingDirectory); // This also tidies up memory allocated by opendir
return 0;
}
int main(int argc, char **argv) {
int counter = 1; // For handling input arguments, although the only one we expect is the dir to
// list.
// enter multiple dirs, get multiple outputs
dirDepth = 0;
if (argc == 1)
listContents(".");
while (++counter <= argc) {
printf("\nListing %s...\n", argv[counter-1]);
listContents(argv[counter-1]);
}
return 0; // Indicate success with zero. Standard practice.
}
|