How to use S_ISREG() and S_ISDIR() POSIX Macros? - c

This is a C program I wrote to recursively navigate and output directories and regular files. It compiles and runs fine on my Linux machine. But on Solaris, the dit->d_type == 8 check and the other similar ones don't work because there is no d_type field. An answer I've read to this problem is to use the S_ISREG() and S_ISDIR() macros, but they don't work at all the way I have them in my code currently. I commented out the lines that work on my Linux machine.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
void helper(DIR *, struct dirent *, struct stat, char *, int, char **);
void dircheck(DIR *, struct dirent *, struct stat, char *, int, char **);
int main(int argc, char *argv[]){
DIR *dip;
struct dirent *dit;
struct stat statbuf;
char currentPath[FILENAME_MAX];
int depth = 0; /*Used to correctly space output*/
/*Open Current Directory*/
if((dip = opendir(".")) == NULL)
return errno;
/*Store Current Working Directory in currentPath*/
if((getcwd(currentPath, FILENAME_MAX)) == NULL)
return errno;
/*Read all items in directory*/
while((dit = readdir(dip)) != NULL){
/*Skips . and ..*/
if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
continue;
if(stat(currentPath, &statbuf) == -1){
perror("stat");
return errno;
}
/*Checks if current item is of the type file (type 8) and no command line arguments
if(dit->d_type == 8 && argv[1] == NULL)*/
if(S_ISREG(statbuf.st_mode) && argv[1] == NULL)
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
/*If a command line argument is given, checks for filename match
if(dit->d_type == 8 && argv[1] != NULL)*/
if(S_ISREG(statbuf.st_mode) && argv[1] != NULL)
if(strcmp(dit->d_name, argv[1]) == 0)
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
/*Checks if current item is of the type directory (type 4)
if(dit->d_type == 4)*/
if(S_ISDIR(statbuf.st_mode))
dircheck(dip, dit, statbuf, currentPath, depth, argv);
}
closedir(dip);
return 0;
}
/*Recursively called helper function*/
void helper(DIR *dip, struct dirent *dit, struct stat statbuf,
char currentPath[FILENAME_MAX], int depth, char *argv[]){
int i = 0;
if((dip = opendir(currentPath)) == NULL)
printf("Error: Failed to open Directory ==> %s\n", currentPath);
while((dit = readdir(dip)) != NULL){
if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
continue;
stat(currentPath, &statbuf);
/*if(dit->d_type == 8 && argv[1] == NULL){*/
if(S_ISREG(statbuf.st_mode) && argv[1] == NULL){
for(i = 0; i < depth; i++)
printf(" ");
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
}
/*if(dit->d_type == 8 && argv[1] != NULL){*/
if(S_ISREG(statbuf.st_mode) && argv[1] != NULL){
if(strcmp(dit->d_name, argv[1]) == 0){
for(i = 0; i < depth; i++)
printf(" ");
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
}
}
/*if(dit->d_type == 4)*/
if(S_ISDIR(statbuf.st_mode))
dircheck(dip, dit, statbuf, currentPath, depth, argv);
}
}
void dircheck(DIR *dip, struct dirent *dit, struct stat statbuf,
char currentPath[FILENAME_MAX], int depth, char *argv[]){
int i = 0;
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
/*If two directories exist at the same level the path
is built wrong and needs to be corrected*/
if((chdir(currentPath)) == -1){
chdir("..");
getcwd(currentPath, FILENAME_MAX);
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
for(i = 0; i < depth; i++)
printf (" ");
printf("%s (subdirectory)\n", dit->d_name);
depth++;
helper(dip, dit, statbuf, currentPath, depth, argv);
}
else{
for(i =0; i < depth; i++)
printf(" ");
printf("%s (subdirectory)\n", dit->d_name);
chdir(currentPath);
depth++;
helper(dip, dit, statbuf, currentPath, depth, argv);
}
}

You're using S_ISREG() and S_ISDIR() correctly, you're just using them on the wrong thing.
In your while((dit = readdir(dip)) != NULL) loop in main, you're calling stat on currentPath over and over again without changing currentPath:
if(stat(currentPath, &statbuf) == -1) {
perror("stat");
return errno;
}
Shouldn't you be appending a slash and dit->d_name to currentPath to get the full path to the file that you want to stat? Methinks that similar changes to your other stat calls are also needed.

[Posted on behalf of fossuser] Thanks to "mu is too short" I was able to fix the bug. Here is my working code has been edited in for those looking for a nice example (since I couldn't find any others online).
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
void helper(DIR *, struct dirent *, struct stat, char *, int, char **);
void dircheck(DIR *, struct dirent *, struct stat, char *, int, char **);
int main(int argc, char *argv[]){
DIR *dip;
struct dirent *dit;
struct stat statbuf;
char currentPath[FILENAME_MAX];
int depth = 0; /*Used to correctly space output*/
/*Open Current Directory*/
if((dip = opendir(".")) == NULL)
return errno;
/*Store Current Working Directory in currentPath*/
if((getcwd(currentPath, FILENAME_MAX)) == NULL)
return errno;
/*Read all items in directory*/
while((dit = readdir(dip)) != NULL){
/*Skips . and ..*/
if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
continue;
/*Correctly forms the path for stat and then resets it for rest of algorithm*/
getcwd(currentPath, FILENAME_MAX);
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
if(stat(currentPath, &statbuf) == -1){
perror("stat");
return errno;
}
getcwd(currentPath, FILENAME_MAX);
/*Checks if current item is of the type file (type 8) and no command line arguments*/
if(S_ISREG(statbuf.st_mode) && argv[1] == NULL)
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
/*If a command line argument is given, checks for filename match*/
if(S_ISREG(statbuf.st_mode) && argv[1] != NULL)
if(strcmp(dit->d_name, argv[1]) == 0)
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
/*Checks if current item is of the type directory (type 4)*/
if(S_ISDIR(statbuf.st_mode))
dircheck(dip, dit, statbuf, currentPath, depth, argv);
}
closedir(dip);
return 0;
}
/*Recursively called helper function*/
void helper(DIR *dip, struct dirent *dit, struct stat statbuf,
char currentPath[FILENAME_MAX], int depth, char *argv[]){
int i = 0;
if((dip = opendir(currentPath)) == NULL)
printf("Error: Failed to open Directory ==> %s\n", currentPath);
while((dit = readdir(dip)) != NULL){
if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
continue;
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
stat(currentPath, &statbuf);
getcwd(currentPath, FILENAME_MAX);
if(S_ISREG(statbuf.st_mode) && argv[1] == NULL){
for(i = 0; i < depth; i++)
printf(" ");
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
}
if(S_ISREG(statbuf.st_mode) && argv[1] != NULL){
if(strcmp(dit->d_name, argv[1]) == 0){
for(i = 0; i < depth; i++)
printf(" ");
printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
}
}
if(S_ISDIR(statbuf.st_mode))
dircheck(dip, dit, statbuf, currentPath, depth, argv);
}
/*Changing back here is necessary because of how stat is done*/
chdir("..");
closedir(dip);
}
void dircheck(DIR *dip, struct dirent *dit, struct stat statbuf,
char currentPath[FILENAME_MAX], int depth, char *argv[]){
int i = 0;
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
/*If two directories exist at the same level the path
is built wrong and needs to be corrected*/
if((chdir(currentPath)) == -1){
chdir("..");
getcwd(currentPath, FILENAME_MAX);
strcat(currentPath, "/");
strcat(currentPath, dit->d_name);
for(i = 0; i < depth; i++)
printf (" ");
printf("%s (subdirectory)\n", dit->d_name);
depth++;
helper(dip, dit, statbuf, currentPath, depth, argv);
}
else{
for(i =0; i < depth; i++)
printf(" ");
printf("%s (subdirectory)\n", dit->d_name);
chdir(currentPath);
depth++;
helper(dip, dit, statbuf, currentPath, depth, argv);
}
}

Related

How to read a directory given in the command line and open and print the directories

I am working on a version of graphical ls, representing the output of ls with a tree. I have gotten most of my code working but would like to be able to determine what directory to read from in the command line. I have tried using
DIR *d
d = opendir(argv[1]);
But this does not work and results in errors opening files as well as the size and other information not updating for the file.
Any information would be greatly appreciated! Thanks.
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#define _GNU_SOURCE
#include <limits.h>
void helper(DIR *, struct dirent *, struct stat, char *, int, char **);
void dircheck(DIR *, struct dirent *, struct stat, char *, int, char **);
int main(int argc, char *argv[]){
DIR *d;
// Create dirent struct
struct dirent *dir;
// Create stat struct
struct stat buf;
// current path for file
char currentPath[FILENAME_MAX];
// Depth for tree
int depth = 0;
// Checking for correct usage
if (argc != 2) {
printf("Usage: './gls <directory_name>'\n");
exit(0);
}
// Checking that file provided in command line actually exists
if (lstat(argv[(argc - 1)], &buf) < 0) {
printf("Error: No such file exists.\n");
return 1;
exit(0);
}
// General resource for printing files: http://stackoverflow.com/questions/4204666/how-to-list-files-in-a-directory-in-a-c-program%20*/
// Open the current directory
d = opendir (".");
if(d == NULL) {
printf("Error opening directory.\n");
return 1;
}
// Store the current directory into currentPath
if((getcwd(currentPath, FILENAME_MAX)) == NULL) {
printf("Error: No such file exists.\n");
return 1;
}
// Iterate through all items in directory
while((dir = readdir(d)) != NULL){
// Do not process . and ..
if(strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
continue;
// Forms the path for lstat
getcwd(currentPath, FILENAME_MAX);
strcat(currentPath, "/");
strcat(currentPath, dir->d_name);
if(lstat(currentPath, &buf) == -1){
perror("stat");
printf("Error could not open file\n");
}
getcwd(currentPath, FILENAME_MAX);
// Checks if file is a regular file
if(S_ISREG(buf.st_mode))
printf("| %s (regular file - %d - !checksum)\n", dir->d_name, (int)buf.st_size);
// Checks if file is a directory
else if(S_ISDIR(buf.st_mode)) {
printf("| %s (directory)\n", dir->d_name);
dircheck(d, dir, buf, currentPath, depth, argv);
}
// Checks if file is a symbolic link
else if(S_ISLNK(buf.st_mode)) {
// Resource used for absolute and relative paths: http://www.apiexamples.com/c/stdlib/realpath.html
char resolved_path[PATH_MAX];
realpath(currentPath, resolved_path);
printf("| %s (symbolic link - %s)\n", dir->d_name, resolved_path);
}
// Checks if file is a FIFO
else if(S_ISFIFO(buf.st_mode)) {
printf("| %s (fifo (named pipe))\n", dir->d_name);
}
}
// Close the directory and return 0 for success
closedir(d);
return 0;
}
// Recursive helper
// Resource used for some of helper function and dircheck function: http://stackoverflow.com/questions/4989431/how-to-use-s-isreg-and-s-isdir-posix-macros
void helper(DIR *d, struct dirent *dir, struct stat buf, char currentPath[FILENAME_MAX], int depth, char *argv[]){
int i = 0;
// Open directory in currentPath
if((d = opendir(currentPath)) == NULL)
printf("Error: Failed to open Directory ==> %s\n", currentPath);
// Read through directory
while((dir = readdir(d)) != NULL){
// If file is . or .. ignore
if(strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
continue;
getcwd(currentPath, FILENAME_MAX);
strcat(currentPath, "/");
strcat(currentPath, dir->d_name);
getcwd(currentPath, FILENAME_MAX);
// If file is a register
if(S_ISREG(buf.st_mode)){
for(i = 0; i < depth; i++) {
printf(" ");
printf("%s (%d bytes)\n", dir->d_name, (int)buf.st_size);
}
}
// If file is a directory
if(S_ISDIR(buf.st_mode)) {
if(strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
dircheck(d, dir, buf, currentPath, depth, argv);
}
}
}
// Change directory back
chdir("..");
closedir(d);
}
// Resource used as guideline in order to create helper and dircheck functions
// http://stackoverflow.com/questions/4989431/how-to-use-s-isreg-and-s-isdir-posix-macros
void dircheck(DIR *d, struct dirent *dir, struct stat buf, char currentPath[FILENAME_MAX], int depth, char *argv[]){
int i = 0;
strcat(currentPath, "/");
strcat(currentPath, dir->d_name);
// If two directories exists at the same section in the tree
if((chdir(currentPath)) == -1){
getcwd(currentPath, FILENAME_MAX);
strcat(currentPath, "/");
strcat(currentPath, dir->d_name);
getcwd(currentPath, FILENAME_MAX);
// Add --- based on the depth of the tree
for(i = 0; i <= depth; i++)
printf ("---");
printf("| %s (subdirectory)\n", dir->d_name);
depth++;
helper(d, dir, buf, currentPath, depth, argv);
}
else{
// Add --- based on the depth of the tree
for(i =0; i <= depth; i++)
printf("---");
printf("| %s (subdirectory)\n", dir->d_name);
chdir(currentPath);
depth++;
helper(d, dir, buf, currentPath, depth, argv);
}
}
You are reading stat before while loop, this is dir in your case.
Then for every file in directory you are checking st_mode, but this is not updated nowhere in the while loop.
if (stat(argv[(argc - 1)], &statBuf) < 0)
this line just query the dir info, so you will always get directory type.
you should query specific file under that dir,
so you can change the code like this:
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
/* Resource: http://stackoverflow.com/questions/4204666/how-to-list-files-in-a-directory-in-a-c-program */
/* Resource: http://cboard.cprogramming.com/linux-programming/131-stat.html */
#define MAX_FILE_NAME_LEN 256
int main(int argc, char *argv[])
{
char path[MAX_FILE_NAME_LEN] = {0};
struct stat statBuf;
if (argc != 2) {
printf("Usage: './gls <directory_name>'\n");
exit(0);
}
if (stat(argv[(argc - 1)], &statBuf) < 0) {
printf("Error: No such file exists.\n");
exit(0);
}
DIR *d;
struct dirent *dir;
//char currentPath[FILENAME_MAX];
d = opendir(argv[1]);
while ((dir = readdir(d)) != NULL) {
//getcwd(currentPath, FILENAME_MAX);
//strcat(currentPath, "/");
//strcat(currentPath, dir->d_name);
//if(stat(currentPath, &statBuf) == -1){
//printf("N")
//}
memset(path, 0, MAX_FILE_NAME_LEN);
snprintf(path,MAX_FILE_NAME_LEN,"%s%s",argv[1],dir->d_name);
//getcwd(currentPath, FILENAME_MAX);
if (stat(path, &statBuf) < 0) {
printf("Error: No such file exists.\n");
continue;
// exit(0);
}
if(S_ISREG(statBuf.st_mode)) {
printf("| %s (regular file - %d - !checksum!)\n", dir->d_name, (int)statBuf.st_size); /* If regular file */
}
if(S_ISDIR(statBuf.st_mode)) {
printf("| %s (directory - size)\n", dir->d_name);
}
if(S_ISLNK(statBuf.st_mode)) {
printf("| %s (symbolic link - points to !!!\n", dir->d_name);
}
if(S_ISFIFO(statBuf.st_mode)) {
printf("| %s (fifo (named pipe))\n", dir->d_name);
}
}
closedir(d);
return(0);
}

What different two recursive directory code?

I saw below code and change only checking file mode, I want to use stat, st_mode. But result is not same. Difference is just checking function.
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
void listdir(const char *name, int level)
{
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(name)))
return;
if (!(entry = readdir(dir)))
return;
do {
if (entry->d_type == DT_DIR) {
char path[1024];
int len = snprintf(path, sizeof(path)-1, "%s/%s", name, entry->d_name);
path[len] = 0;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
printf("%*s[%s]\n", level*2, "", entry->d_name);
listdir(path, level + 1);
}
else
printf("%*s- %s\n", level*2, "", entry->d_name);
} while (entry = readdir(dir));
closedir(dir);
}
int main(void)
{
listdir(".", 0);
return 0;
}
and just change check file_mode
void filelist(const char*loc, int dep){
DIR*dirpt;
struct dirent* dir;
if (!(dirpt = opendir(loc)))
return;
while((dir = readdir(dirpt))!=NULL)
{
struct stat buf;
lstat(dir->d_name, &buf);
if(S_ISDIR(buf.st_mode)){
char p[1024];
int l = snprintf(p, sizeof(p)-1, "%s/%s", loc, dir->d_name);
p[l] = 0;
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
continue;
printf("%*s[%s]\n", dep*2,"", dir->d_name);
filelist(p,dep + 1);
}
else
printf("%*s- %s\n", dep*2,"", dir->d_name);
}
closedir(dirpt);
}
int main(void){
filelist(".",0);
return 0;
}
but result is not same, cannot exploring all directory....I don't know why....
lstat(dir->d_name, &buf);
In this call, dir->d_name does not capture the complete path. It is only the name of the entry in that directory. You should capture the return value of the function and make sure that it was able to get the values you need.
if ( lstat(dir->d_name, &buf) == -1 )
{
// Deal with error
}
You'll have to use something like:
while((dir = readdir(dirpt))!=NULL)
{
char p[1024];
struct stat buf;
int l = snprintf(p, sizeof(p)-1, "%s/%s", loc, dir->d_name);
p[l] = 0;
if ( lstat(dir->d_name, &buf) == -1 )
{
// Problem
continue;
}
if(S_ISDIR(buf.st_mode)){
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
continue;
printf("%*s[%s]\n", dep*2,"", dir->d_name);
filelist(p,dep + 1);
}
else
printf("%*s- %s\n", dep*2,"", dir->d_name);
}

C function, print to buffer

I have a client/ server application, and i get an error there -> perror ("[server] Can't send the message to client.\n"). So the server cant' send the msgrasp ( the buffer). If you can help, I'll be grateful.
void printdir(char *dir, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
printf("%*s%s/\n",depth,"",entry->d_name);
/* Recurse at a new indent level */
printdir(entry->d_name,depth+4);
}
else printf("%*s%s\n",depth,"",entry->d_name);
}
chdir("..");
closedir(dp);
}
int printForClient(int fd)
{
char buffer[100];
int bytes;
char msg[100];
char *msgrasp=NULL;
bytes = read (fd, msg, sizeof (buffer));
if (bytes < 0)
{
perror ("Can't read from client.\n");
return 0;
}
printf ("[server]..%s\n", msg);
printdir(&msgrasp,msj,0);
printf("[server]%s\n",msgrasp);
if (bytes && write (fd, msgrasp, bytes) < 0)
{
perror ("[server] Can't send the message to client.\n");
return 0;
}
return bytes;
}
This is not a very good Idea, but since it's what you requested here you have a solution with dynamic memory allocation
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
void printdir(char **output, const char *const dir, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if ((dp = opendir(dir)) == NULL)
{
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while ((entry = readdir(dp)) != NULL)
{
/* check if stat succeeded */
if (lstat(entry->d_name, &statbuf) == -1)
continue;
if (S_ISDIR(statbuf.st_mode) != 0)
{
char *buffer;
size_t length;
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
length = 4 + depth + strlen(entry->d_name);
if (*output != NULL)
length += strlen(*output);
buffer = realloc(*output, length);
if (buffer != NULL)
{
char current[length];
if (*output == NULL)
buffer[0] = '\0';
*output = buffer;
snprintf(current, length, "%*s%s/\n", depth, " ", entry->d_name);
strcat(*output, current);
//printf("%*s%s/\n",depth,"",entry->d_name);
/* Recurse at a new indent level */
}
else
{
fprintf(stderr, "Out of memory\n");
free(*output);
*output = NULL;
closedir(dp);
return;
}
printdir(output, entry->d_name, depth + 4);
}
else
{
char *buffer;
size_t length;
length = 4 + depth + strlen(entry->d_name);
if (*output != NULL)
length += strlen(*output);
buffer = realloc(*output, length);
if (buffer != NULL)
{
char current[length];
if (*output == NULL)
buffer[0] = '\0';
*output = buffer;
snprintf(current, length, "%*s%s/\n", depth, " ", entry->d_name);
strcat(*output, current);
}
else
{
fprintf(stderr, "Out of memory\n");
free(*output);
*output = NULL;
closedir(dp);
return;
}
}
}
chdir("..");
closedir(dp);
}
int main()
{
char *buffer = NULL; //buffer for printdir output.
printf("Directory scan of /home:\n");
printdir(&buffer, "/home", 0);
printf("done.\n");
printf("%s\n", buffer);
free(buffer);
exit(0);
}
It is not a very good Idea because you don't know if there will be enough memory to hold all the text, it could grow a lot, I added checks for that situation in this code, in the event of going out of memory, this function will abort reading, release resources and return.
I would suggest adding an argument of type char* (a pointer to a char array) and at every recursion advance the pointer. My try (supposing the buffer is long enough for the output):
void printdir(char *dir, int depth, char* output)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
char tmp[100];
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
sprintf(tmp, "%*s%s/\n",depth,"",entry->d_name);
strcpy(buffer, tmp);
/* Recurse at a new indent level */
printdir(entry->d_name,depth+4, buffer+strlen(tmp));
}
else printf("%*s%s\n",depth,"",entry->d_name);
}
chdir("..");
closedir(dp);
}
int main()
{ char buffer[1000]; //buffer for printdir output.
printf("Directory scan of /home:\n");
printdir("/home",0);
printf("done.\n");
exit(0);
}
You might want to add \n between every execution, depending on what you wish to do with the output. Please note that in this program \0 at the end of each string is deleted.

Traversing a Path in c

I am writing a program in c that takes a path name and traverses that path, and prints out all file paths it comes across and the size of that file in blocks and then if it is a dir it prints out the dir pathname and size in blocks.
The code is ending up in an infinate loop and keeps saying "Failed to get status:too many files open."
#include <dirent.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char **argv ){
struct stat statbuf;
struct dirent *direntp;
DIR *dirp;
if(stat(argv[1], &statbuf) == -1){
perror("Failed to get file status");
return 1;
}
else if(argc != 2){
perror("Invalid amount of arguments, showtreesize requires 1 pathname");
return 1;
}
else{
if(S_ISDIR(statbuf.st_mode) || S_ISREG(statbuf.st_mode)){
printf("%s %d", argv[1], depthfirstapply(argv[1], sizepathfun(argv[1])));
}
else{
if(S_ISCHR(statbuf.st_mode)){
printf("%s is a character special file.", argv[1]);
}
if(S_ISBLK(statbuf.st_mode)){
printf("%s is a block special file", argv[1]);
}
if(S_ISFIFO(statbuf.st_mode)){
printf("%s is a FIFO special file", argv[1]);
}
else{
printf("%s is not a valid filetype", argv[1]);
}
}
return 0;
}
}
int sum = 0;
int levelcount = 0;
int isDirectory(char *path){
struct stat statbuf;
if(stat(path, &statbuf) == -1)
return 0;
else
return S_ISDIR(statbuf.st_mode);
}
int depthfirstapply(char *path, int pathfun(char *path1)){
struct dirent *direntp;
DIR *dirp;
if(isDirectory(path)){
printf("%s\n", path);
if((dirp = opendir(path)) == NULL){
perror ("Failed to open directory");
return -1;
}
else{
while((direntp = readdir(dirp)) != NULL) {
if(isDirectory(direntp->d_name)){
int result = depthfirstapply(direntp->d_name, pathfun);
if (result > 0){
sum += result;
}
}
else{
if(pathfun(direntp->d_name) >= 0){
sum += pathfun(direntp->d_name);
}
}
}
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
}
}
else{
sum += pathfun(path);
}
return sum;
}
int sizepathfun(char *path){
struct stat statbuf;
if(stat(path, &statbuf) == -1){
perror("Failed to get file status");
return -1;
}
if(S_ISREG(statbuf.st_mode) == 0){
return -1;
}
else{
printf("%s %d", path, statbuf.st_blocks);
return statbuf.st_blocks;
}
}
Several problems:
You need to skip the . and .. entries. Otherwise, you'll keep looping on the same directory.
Use lstat() rather than stat() in isDirectory. Otherwise, you'll recurse into symbolic links to directories, which could cause a loop.
As you go down each directory level, you need to concatenate the directory components to the names.
The 2nd argument to depthfirstapply is supposed to be a function. But in main() you call it with sizepathfun(argv[1)), which returns an integer. The argument should just be sizepathfun. You should be getting a compilation warning because of the type mismatch.
POSIX provides a standard set of functions for this, fts_XXX().

how can I get the number of directories in a directory?

I am trying to get the number of directories in a folder except the files in but I cannot get the correct result. Somebody help me to solve this problem? Especially what should I sent to the isDirectory() function?
int listFilesIndir(char *currDir)
{
struct dirent *direntp;
DIR *dirp;
int x ,y =0 ;
if ((dirp = opendir(currDir)) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
x= isDirectory(dirp);
if(x != 0)
y++;
}
printf("direc Num : %d\n",y );
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
int isDirectory(char *path)
{
struct stat statbuf;
if (stat(path, &statbuf) == -1)
return 0;
else
return S_ISDIR(statbuf.st_mode);
}
You're sending a directory stream to the function, and treating it like a path.
Linux and some other Unix systems include a way to get this info directly:
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
if (direntp->d_type == DT_DIR)
y++;
}
Otherwise, make sure you send the right details to the function, i.e.
x= isDirectory(direntp->d_name);
The call for your function is wrong.
x= isDirectory(dirp);
While the prototype of function is:
int isDirectory(char *path)
It need a string as parameter, but you give it a "DIR *dirp;". I changed the code as:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int listFilesIndir(char *currDir)
{
struct dirent *direntp;
DIR *dirp;
int x ,y =0 ;
if ((dirp = opendir(currDir)) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
if(direntp->d_type == DT_DIR)
y++;
}
printf("direc Num : %d\n",y );
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
int main(int argc, char **argv){
if(argc == 2){
// Check whether the argv[1] is a directory firstly.
listFilesIndir(argv[1]);
}
else{
printf("Usage: %s directory", argv[0]);
}
return 0;
}
I tested it on my Linux server. And it works well. SO #teppic is right. But pay attention, in the code, the number of directory includes two specific ".." (parent directory) and "." (current directory). If you do not want to include it, you could change:
printf("direc Num : %d\n",y );
into:
printf("direc Num : %d\n",y-2 );
Hope it helps!

Resources