I have a problem opening the same directory on second call.
For example i first open folder1/folder2; then if i call the function i'm using on folder1 it says it cannot open it. I though I would close all directories in a path and tried to do it but with no results.
This is my code
void scanDir(char *dir, int depth, char type, char *path, long gtsize, int attrib)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
char newPath[strlen(path)+strlen(dir)];
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"Cannot open directory %s\n because of e", dir);
exit(10);
return;
}
strcpy(newPath, path);
strcat(newPath, dir);
if (type!='f' && testAttrib(attrib, dir))
printf("%s\n", newPath);
strcat(newPath, "/");
chdir(dir);
while((entry = readdir(dp)) != NULL) {
stat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode) && testAttrib(attrib, entry->d_name)) {
if(!strcmp(".",entry->d_name) || !strcmp("..",entry->d_name))
continue; // ignore . and ..
if (depth>1 || depth<=-1)
scanDir(entry->d_name,depth-1,type,newPath,gtsize,attrib);
}
if(S_ISREG(statbuf.st_mode) && type!='d' && testAttrib(attrib, entry->d_name)) {
off_t sizeF = statbuf.st_size;
char filePath[100];
strcpy(filePath, newPath);
strcat(filePath, entry->d_name);
if(sizeF>=gtsize)
printf("%s \n", filePath);
}
}
chdir("..");
closedir(dp);
}
char newPath[strlen(path)+strlen(dir)]; //WRONG!
is certainly wrong. You need to reserve one extra byte for the terminating 0 and you are adding a /. So it should be
char newPath[strlen(path)+strlen(dir)+2];
BTW, consider using snprintf(3) or asprintf(3) instead of your strcat calls.
I am not sure that calling chdir(2) is a wise idea, and you certainly should check that it went well. See perror(3), errno(3), strerror(3).
Look also into nftw(3).
in struct dirent, member d_name contains name without path. This means that parameter passed to the function, opendir(), does not have path to file or directory so that error ENOENT happens.
lets say you have directory /home/usr/folder1/folder2. and you call
scandir("/home/usr/folder1/", 2, type, ...) // I understood only first two parameters.
this functions seems to work but when the function calls itself recursively to search /home/usr/folder1/folder2
if (depth>1 || depth<=-1)
scanDir(entry->d_name,depth-1,type,newPath,gtsize,attrib);
the first parameter passed to the scandir this time is "folder2" not "/home/usr/folder1/folder2" so opendir(dir) gives error named ENOENT
and one more thing you should be careful is that readdir() function is not reentrant function so that calling readdir() function may result in not expected error. in your code, it looks like the function gives result as you want. However, I think "how it works" may be different from what you think. if the code becomes complicated, i recommends to use readdir_r() function which is re-entrant version of readdir
according to the man page of readdir:
On success, readdir() returns a pointer to a dirent structure. (This
structure may be statically allocated; do not attempt to free(3) it.)
after recursively calling scandir() function, in each stack of function, your entry becomes NUll pointer since the structure is statically allocated.
another suggestion for you function is that using nftw() or scandir() functions offered by linux. especially nftw is really powerful and does most of you want.
Related
I'm learning C. so I'm just kinda confused about the function readdir. In the book K&R, the function dirwalk includes the following
while ((dp = readdir(dfd)) != NULL){
if (strcmp(dp->name, ".") == 0
//...code...
Based on my understanding, each time the whileloop is passed, dp (directory entry) is advanced one step, so next directory entry (which is associated with a file) can be processed (while dp != NULL)
My question is: How doesreaddir return a new directory entry each time it's called? Where does it show that? Please don't use too much jargon as I just started learning about this. Here's the code for readdir. Thanks.
#include <sys/dir.h>
Dirent *readdir(DIR *dp)
{
struct direct dirbuf; \* local directory structure *\
static Dirent d;
while (read(dp->fd, (char *) &dirbuf, sizeof(dirbuf))
== sizeof(dirbuf)) {
if (dirbuf.d_ino == 0) \* slot not in use *\
continue;
d.ino = dirbuf.d_ino;
strncpy(d.name, dirbuf.d_name, DIRSIZ);
d.name[DIRSIZ] = '\0'; \* ensure termination *\
return &d;
}
return NULL;
}
First, this is not the code that the POSIX readdir would use on any relevant operating system...
How the code works is really simple, assuming that you know how files work. A directory on the UNIX systems is just as readable a file as any other file would be - a directory would appear as if a binary file of directory records - in this implementation the dirbuf structure is one record in a directory. Therefore reading sizeof dirbuf bytes from the file descriptor gives you a next entry from the directory - the filename and its associated inode number.
If a file is deleted an entry might be marked unused by setting the inode number to 0, and it is skipped by the code.
When a next used entry is found, its filename and inode number is copied to the Dirent d, which has static storage duration. It means that there is only one Dirent allocated for use by readdir for the entire duration of the program. readdir will return the same pointer over and over again, pointing to the same structure, but the contents of the structure change.
Finally, when all entries in the directory have been read the last call to readdir will execute a read that will not read sizeof (dirbuf) bytes and the loop is broken, and NULL pointer is returned.
The following is my code for a method that copies a file from a path to a file to a directory provided as the destination. The copy works perfectly fine, however my chmod call assigns the wrong permissions to the copied file in the destination. If the permission in the source is 644, the copied file has a permission of 170 or 120.
I have been attempting to debug this for hours and it's driving me slightly crazy so any help is greatly appreciated.
void copy_file(char* src, char* dest) {
char a;
//extract file name through a duplicate ptr
char* fname = strdup(src);
char* dname = basename(fname);
//open read and write streams
FILE* read;
FILE* write;
read = fopen(src, "r");
chdir(dest);
write = fopen(dname, "w");
//error checking
if (read == NULL) //|| (write == NULL))
{
perror("Read Error: ");
exit(0);
}
else if (write == NULL)
{
perror("Write Error: ");
exit(0);
}
//write from src to dest char by char
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
//close files
fclose(read);
fclose(write);
// this is where I attempt to assign source file permissions
//and it goes horribly wrong
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
chmod(dname, src_st.st_mode);
printf("%o\n", src_st.st_mode & 0777);
}
You fopen(src, "r"), then you chdir(dest). This means that when you later call stat(src, &src_st), there is no reason to think that stat will access the same file as fopen did, or indeed that stat will access any file at all.
If stat fails, you proceed to call chmod anyway, so you pass whatever random junk was in src_st.st_mode to chmod.
You should use fstat(fileno(read), &src_st) before calling fclose(src), instead of calling stat(src, &src_st).
The basic problem is you have to check your system calls like fopen, chdir, and stat immediately.
For example, first thing I tried was copy_file( "test.data", "test2.data" ) not realizing it expected a destination directory.
char* fname = strdup(src);
char* dname = basename(fname);
dname is now test.data, same as the source.
read = fopen(src, "r"); // succeeds
chdir(dest); // fails
write = fopen(dname, "w"); // blows away test.data, the source
You do eventually check read and write, but after the damage has been done.
Blowing away your source file is really bad. It's important that your code deals with failed system calls. If you don't, it will sail along causing confusion and destruction.
Most system calls in C return 0 for success. This is an anti-pattern where the return value is an error flag, so false is failure, and anything else indicates what kind of error (though stat doesn't use that, it uses errno).
When it fails, stat returns -1 which is true. So this is the wrong way around.
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
Instead, you have to check for non-zero.
struct stat src_st;
if(stat(src, &src_st) != 0 ){
// Note that I don't use perror, it doesn't provide enough information.
fprintf(stderr, "Could not stat %s: %s\n", src, strerror(errno));
exit(1);
}
As you can guess this gets tedious in the extreme, and you're going to forget, or do it slightly different each time. You'll want to write wrappers around those functions to do the error handling for you.
FILE *fopen_checked( const char *file, const char *mode ) {
FILE *fp = fopen(file, mode);
if( file == NULL ) {
fprintf(stderr, "Could not open '%s' for '%s': %s", file, mode, strerror(errno));
exit(1);
}
return fp;
}
It's not the best error handling, but it will at least ensure your code appropriately halts and catches fire.
A note about chdir: if you can avoid it don't use it. chdir affects the global state of the program, the current working directory, and globals add complexity to everything. It's very, very easy for a function to change directory and not change back, as yours does. Now your process is in a weird state.
For example, if one did copy_file( "somefile", "foo" ) this leaves the program in foo/. If they then did copy_file( "otherfile", "foo" ) they'd be trying to copy foo/otherfile to foo/foo/otherfile.
And, as #robmayoff pointed out, your stat fails because the process is now in a different directory. So even the function doing the chdir is confused by it.
Ensuring that your functions always chdir back to the original directory in a language like C is very difficult and greatly complicates error handling. Instead, stay in your original directory and use functions like basename to join paths together.
Finally, avoid mixing your file operations. Use filenames or use file descriptors, but try not to use both. That means if you're using fopen, use fstat and fchmod. You might have to use fileno to get a file descriptor out of the FILE pointer.
This avoids having to carry around and keep in sync two pieces of data, the file descriptor and the filename. It also avoids issues with chdir or the file being renamed or even deleted, the file descriptor will still work so long as it remains open.
This is also a problem:
char a;
...
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
fgetc() returns int, not char. Per the C Standard, 7.21.7.1 The fgetc function:
7.21.7.1 The fgetc function
Synopsis
#include <stdio.h>
int fgetc(FILE *stream);
Assuming sizeof( int ) > sizeof( char ), char values are signed, 2s-complement integers, and EOF is an int defined to be -1 (all very common values), reading a file with char a = fgetc( stream ); will fail upon reading a valid 0xFF character value. And if your implementation's default char value is unsigned char, char a = fgetc( stream ); will never produce a value that matches EOF.
I have the following in my code: (Coding in c)
ftw(argv[2], parseFile, 100)
argv[2] is a local directory path. For instance. argv[2] = "TestCases" and there is a testcases folder in the same directory as my .o file.
My understanding is that this should traverse the directory TestCases and send every file it finds to the function parseFile.
What actually happens is it simply sends my argument to the function parseFile and that is all. What am I doing wrong? How am I suppose to use this properly?
EDIT: This is parseFile:
int parseFile(const char * ftw_filePath,const struct stat * ptr, int flags){
FILE * file;
TokenizerT * currFile;
char fileString[1000], * currWord, * fileName;
fileName = strdup(ftw_filePath);
if( fileName == NULL || strlen(fileName) <= 0){
free(fileName);
return -1;
}
printf("\n%s\n",fileName);
if(strcmp(fileName,"-h")== 0){
printf("To run this program(wordstats) type './wordstat.c' followed by a space followed by the file's directory location. (e.g. Desktop/CS211/Assignment1/test.txt )");
free(fileName);
return 1;
}
else{
file=fopen(fileName,"r");
}
if(!file){
fprintf(stderr,"Error: File Does not Exist in designated location. Please restart the program and try again.\n");
free(fileName);
return 0;
}
memset(fileString, '\0', 1000);
while(fscanf(file,"%s", fileString) != EOF){ /* traverses the file line by line*/
stringToLower(fileString);
currFile = TKCreate("alphanum",fileString);
while((currWord = TKGetNextToken(currFile)) != NULL) {
insert_List(currWord, words,fileName);
}
free(currFile->delimiters);
free(currFile->copied_string);
free(currFile);
memset(fileString, '\0', 1000);
}
fclose(file);
free(fileName);
return 1;
}
It will work if I input TestCases/big.txt for my argv[2] but not if I put TestCases
As described in the man page, a non-zero return value from the function that ftw is calling tells ftw to stop running.
Your code has various return statements, but the only one that returns 0 is an error condition.
A properly designed C callback interface has a void* argument that you can use to pass arbitrary data from the surrounding code into the callback. [n]ftw does not have such an argument, so you're kinda up a creek.
If your compiler supports thread-local variables (the __thread storage specifier) you can use them instead of globals; this will work but is not really that much tidier than globals.
If your C library has the fts family of functions, use those instead. They are available on most modern Unixes (including Linux, OSX, and recent *BSD)
I'm not sure if C can do this, but I'm hoping that I can make a program that will look into a directory, and print out all of the contents of the directory along with the file size of each file. As in I wanted it to look like this (possibly):
filename.txt -- 300 bytes
filename2.txt -- 400 bytes
filename3.txt -- 500 bytes
And so on.
So far, I created a program that can open a file, and it will print the bytes, but it does not read the entire directory, and I have to be specific with which file I want to read.. (which is not what I want).
Here is what I have so far:
#include <stdio.h>
int main(){
FILE *fp; // file pointer
long fileSize;
int size;
// opens specified file and reads
fp = fopen( "importantcommands.txt", "rw" );
if( fp == NULL ){
printf( "Opening file error\n" );
return 0;
}
// uses fileLength function and prints here
size = fileLength(fp);
printf( "\n Size of file: %d bytes", size );
fclose(fp);
return 0;
}
int fileLength( FILE *f ){
int pos;
int end;
// seeks the beginning of the file to the end and counts
// it and returns into variable end
pos = ftell(f);
fseek (f, 0, SEEK_END);
end = ftell(f);
fseek (f, pos, SEEK_SET);
return end;
}
Please help.
C can certainly do it - the ls(1) command can, for example, and it's written in C.
To iterate over a directory, you can use the opendir(3) and readdir(3) functions. It's probably easier to just let the shell do it for you, though.
As far as getting the filename, you can just take it as a command line parameter by defining main as:
int main(int argc, char **argv)
Command line parameters will begin at argv[1].
See opendir() / fdopendir() and readdir() if you are using linux in dirent.h
man page
Simple example from a : SO Post
DIR *dir;
struct dirent *ent;
if ((dir = opendir ("c:\\src\\")) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
printf ("%s\n", ent->d_name);
}
closedir (dir);
}
else {
/* could not open directory */
perror ("Could not open directory");
return EXIT_FAILURE;
}
Also You can use the fstat() system call which can fill in the struct stat for any file you want. From that stat you can access that file's size.
Please use the man pages to help you out. (Almost) Everything related to Linux is insanely well documented.
To read a list of files in a directory look at opendir, readdir, closedir for Linux
use stat to get the length of the file.
These are of Linux
For winodws see http://msdn.microsoft.com/en-gb/library/windows/desktop/aa365200%28v=vs.85%29.asp and the link http://blog.kowalczyk.info/article/8f/Get-file-size-under-windows.html will show you how to do this.
To get the list of files in a directory look for "libc opendir". To get the size of a file without opening it you can use fstat.
This seems strangely similar to another question I saw recently. Anyway, here's my strangely similar answer (for Linux, not sure how it'll fare on Windows 7):
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
struct stat file_stats;
DIR *dirp;
struct dirent* dent;
dirp=opendir("."); // specify directory here: "." is the "current directory"
do {
dent = readdir(dirp);
if (dent)
{
printf("%s -- ", dent->d_name);
if (!stat(dent->d_name, &file_stats))
{
printf("%u bytes\n", (unsigned int)file_stats.st_size);
}
else
{
printf("(stat() failed for this file)\n");
}
}
} while (dent);
closedir(dirp);
}
There are little things need to be taken care for the given examples (under Linux or other UNIX).
You properly only want to print out the file name and size of a regular file only. Use S_ISREG() to test the st_mode field
If you want to recursively print out all files under sub directories also, you then need to use S_ISDIR() to test for direcotry and be carefull of special directory '.' and '..'.
I'm having some trouble with the 'if(S_IFDIR(stbuf.st_mode))' line. Is this the correct way to test for a directory to recurse into? The function at the moment seems to do it right for 1 or 2 loops and then fails and segmentation faults.
I've tried the following and probably more as the condition.
S_ISDIR(st_mode)
((st_mode & ST_IFMT) == S_IFDIR)
S_IFDIR(stbuf.st_mode)
I've included the whole function because I'm concerned the problem might be elsewhere.
void getFolderContents(char *source, int temp){
struct stat stbuf;
int isDir;
dirPnt = opendir(source);
if(dirPnt != NULL){
while(entry = readdir(dirPnt)){
char *c = entry->d_name;
if(strcmp(entry->d_name, cwd) == 0 || strcmp(entry->d_name, parent) == 0){
}
else{
stat(entry->d_name, &stbuf);
printf("%i %i ", S_IFMT, stbuf.st_mode);
if(S_IFDIR(stbuf.st_mode)){ //Test DIR or file
printf("DIR: %s\n", entry->d_name);
getFolderContents(entry->d_name, 0);
}
printf("FILE: %s\n", entry->d_name);
}
}
closedir(dirPnt);
}
Yes, that's correct. But since you never change into the directory, you will not find it.
Consider the following directory hierarchy:
a
|
+- b
| |
| +- c
...
Your code will scan its current directory, and find "a". It will determine that it is a directory, and call itself recursively, and open "a" for reading. This works. That scan will find a directory called "b", but trying to open it using the entry name only will fail, since the path is now "a/b".
I recommend changing into the directory (with chdir()) before opening it. That means you can just opendir("."). Store the old path, and chdir() out again when recursing that level is done (not before doing a recursive call to go deeper).
Where is entry defined ? is it a local variable ?
I can't see why it would segfault, but may be you should make it a local variable.
One example where it will bite you is here :
if(S_IFDIR(stbuf.st_mode)){ //Test DIR or file
printf("DIR: %s\n", entry->d_name);
getFolderContents(entry->d_name, 0);
}
printf("FILE: %s\n", entry->d_name);
The printf is gonna print the wrong name, so you should probably add an else here.
The same is true with dirpnt. When you go out of getFolderContents inside the while loop,
you end up calling readdir on a closed dirpoint, which should get you out of the loop.
But as stated by bahbar :
You can't recurse and store temporary variable in global variable