I am trying to read directories recursively and print out some metadata about the files. I have the program work for the single directory. But for the sub-directories, when I apply the stat method, the files keep giving an error of no such file or directory exists.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
void scan( const char *dir) // process one directory
{
DIR *dp;
struct dirent *de;
struct stat sbuf;
dp = opendir( dir);
if( dp == NULL)
{
// perror( dir);
printf("Cannot open directory %s\n",dir);
return;
}
while( 1)
{
const char * d_name;
de = readdir( dp);
if( de == NULL)
break;//Empty Directory
d_name = de->d_name;
printf("d_name: %s\n",d_name);
// if(strcmp(d_name,"..") != 0 && strcmp(d_name,".") != 0)
// printf("%s/%s\n",dir,d_name);//Print File or Directory
if( stat( de->d_name, &sbuf) )
{
// perror( de->d_name);
printf("Error in stat %s\n",de->d_name);
continue;
}
if( S_ISDIR(sbuf.st_mode) && (strcmp(d_name,"..") != 0 && strcmp(d_name,".") != 0))
{
// printf("d_name: %s\n",d_name);
printf( "d\t");
}
else
if (strcmp(d_name,"..") == 0 || strcmp(d_name,".") == 0)
{
// printf("d_name: %s\n",d_name);
// continue;
}
else
{
// printf("d_name tab: %s\n",d_name);
printf( "\t");
}
if(strcmp(d_name,"..") != 0 && strcmp(d_name,".") != 0)
printf( "%lu\t%s\n", (unsigned long) sbuf.st_size, de->d_name);
if(de->d_type == DT_DIR)
{
if(strcmp(d_name,"..") != 0 && strcmp(d_name,".") != 0)
{
char path[1024];
snprintf(path,1024,"%s/%s",dir,d_name);
scan(path);
}
}
}
closedir( dp);
}
int main( int argc, char *argv[])
{
int i;
scan(argv[1]);
return 0;
}
You forgot to prepend your dir to your de->d_name when stating. You're still in your original directory, as you haven't chdired elsewhere.
Related
The last Code I just posted now works. That is, it is able to copy all files from one directory to another. But now, I wanted to update it in such a way that it copies also directories including it contents be it files or folders.
Here is what I did so far, but this has been unable to accomplish my dream.
I really don't know what is wrong with the code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#define Max 8192
int copy_files(char *src, char *dest);
int copy_dir(char *srcpath, char *destpath);
int copy_dir(char *srcpath, char *destpath)
{
DIR *sdp = NULL;
DIR *ddp = NULL;
struct dirent *entry;
struct stat sb;
char tempsrc[strlen(srcpath)+1];
char tempdest[strlen(destpath)+1];
strcat(srcpath, "/");
strcat(destpath, "/");
strcpy(tempdest, destpath);
strcpy(tempsrc, srcpath);
if( (sdp = opendir(srcpath)) == NULL )
{
printf ("%s is not an existing directory\n", srcpath);
return 0;
}
else
{
while( (entry = readdir(sdp)) )
{
stat(entry->d_name, &sb);
// printf("Cannot open directory\n");
// exit(EXIT_FAILURE);
switch (sb.st_mode & S_IFMT)
{
case S_IFREG:
{
strcat(tempdest, entry->d_name);
strcat(tempsrc, entry->d_name);
copy_files(tempsrc, tempdest);
strcpy(tempdest, destpath);
strcpy(tempsrc, srcpath);
break;
}
case S_IFDIR:
{
strcat(tempsrc, entry->d_name);
strcat(tempdest, entry->d_name);
mkdir(tempdest, 0777);
ddp = opendir(tempdest);
copy_dir(tempsrc, tempdest);
strcpy(tempdest, destpath);
strcpy(tempsrc, srcpath);
break;
}
}
}
closedir(sdp);
closedir(ddp);
return 1;
}
}
int copy_files(char *src, char *dest)
{
int sfd, dfd, ret_in, ret_out;
char buff[Max];
if ( (sfd = open(src, O_RDONLY)) == -1 )
{
printf("Error while reading %s\n", src);
perror(src);
exit(1);
}
if ( (dfd = creat(dest, 0644)) == -1 )
{
printf("Error while creating %s\n", dest);
perror(dest);
exit(1);
}
while( (ret_in = read(sfd, &buff, Max)) > 0 )
{
ret_out = write (dfd, &buff, ret_in);
if (ret_out != ret_in)
{
printf("write error to %s", dest);
perror(dest);
exit(1);
}
if (ret_in == -1)
{
printf("read error from %s", src);
perror(src);
exit(1);
}
}
close(sfd);
close(dfd);
return 1;
}
int main(int argc, char *argv[])
{
int i;
if (argc != 3)
{
printf ("Usage: Programme_name src dest\n e.g. ./cp src dest\n");
exit(1);
}
char *srcp = argv[1];
char *destp = argv[2];
if (srcp[0] == '/' && destp[0] == '/')
{
for (i = 1; i <= strlen(destp); i++)
destp[(i-1)] = destp[i];
for (i = 1; i <= strlen(srcp); i++)
srcp[(i-1)] = srcp[i];
copy_dir(srcp, destp);
}
else if (srcp[0] != '/' && destp[0] == '/') //./ass1 test /t2
{
for (i = 1; i <= strlen(destp); i++)
destp[i-1] = destp[i];
strcat(destp, "/");
strcat(destp, srcp);
copy_files(srcp, destp);
}
else
{
printf ("Usage: Programme_name src dest\n e.g. ./cp src dest\n");
exit(1);
}
}
You are indefinitely adding /. to the temporary source and destination paths when the directory entry . is read, which is present in all directories. Instead, you should skip the . and .. entries.
Another error is the dimensioning of the temporary paths:
char tempsrc[strlen(srcpath)+1];
char tempdest[strlen(destpath)+1];
The arrays are made just long enough to hold the original paths, though sub-directory names are then appended, overflowing the arrays. Better:
char tempsrc[PATH_MAX];
char tempdest[PATH_MAX];
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);
}
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.
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!
Here's part of code for my application I'm porting to windowsXp initially written on Ubuntu.
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
int list_files(char *parentdir)
{
DIR *dir;
struct dirent *de = NULL;
char subdirs[1000][1000];
int isubdirs = 0;
char rootdir[1000];
strcpy(rootdir, parentdir);
FILE *fp = fopen("list.txt", "w");
seek:
dir = opendir(rootdir);
if (!dir) {
printf("ERROR: [ %s ]", rootdir);
perror("Couldn't opendir: ");
}
else {
de = readdir(dir);
if (!de) {
printf("ERROR:[ %s ]", rootdir);
perror("Couldn't readdir ");
goto out;
}
if (strcmp(de->d_name, "..") != 0 && strcmp(de->d_name, ".") != 0) {
fprintf(fp, "%s\\%s\n", rootdir, de->d_name);
printf("ADDED: %s\\%s\n", rootdir, de->d_name);
}
while ((de = readdir(dir)) != NULL)
{
if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
continue;
fprintf(fp, "%s\\%s\n", rootdir, de->d_name);
printf("ADDED: %s\\%s\n", rootdir, de->d_name);
struct stat s;
stat(de->d_name, &s);
if (S_ISDIR(s.st_mode))
{
strcpy(subdirs[isubdirs], rootdir);
strcat(subdirs[isubdirs], "\\");
strcat(subdirs[isubdirs], de->d_name);
printf("%s\n", subdirs[isubdirs]);
isubdirs++;
}
/* Not working in WinXP
if (de->d_type == 4) {
strcpy(subdirs[isubdirs], rootdir);
strcat(subdirs[isubdirs], "/");
strcat(subdirs[isubdirs], de->d_name);
isubdirs++;
}
*/
}
if (isubdirs > 0) {
strcpy(rootdir, subdirs[--isubdirs]);
goto seek;
}
}
out:
fclose(fp);
return 0;
}
Now problem is in output file i get:
C:\Documents and Settings\user\Desktop\New Folder\00.txt
C:\Documents and Settings\user\Desktop\New Folder\1
C:\Documents and Settings\user\Desktop\New Folder\2
Only files in parentdir listed, but not in subdirs (1 and 2 both not empty "folders")
I'm testing in Virtualbox.