I am using the stat.h header and stat() to read and list files and directories in a directory. I've tried my code with different preset directories to see the result and print the corresponding type of the entry and so far in each test the code shows the current directory and 2 sub directories as directories but shows the other directories as files despite having the correct path listed alongside it
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <locale.h>
#include <sys/stat.h>
#include <wchar.h>
extern int errno;
int main(int argc, char *argv[]) {
setlocale(LC_ALL,"Turkish");
DIR* dp;
struct dirent dirp;
char test[9999];
getcwd(test,9999);
printf("FOLDERMAIN:%s\n",test);
dp=opendir(".");
searchDirectoryForString(dp,"Mat",test);
return 0;
}
void searchDirectoryForString(DIR* dp,char* str,char* directoryname)
{
int lineno=1,num=0;
FILE* fp;
struct dirent *file;
char temp[99999];
char buf[99999];
char *rpath;
rpath = calloc(99999,1);
char path[99999];
char* temppath;
temppath = calloc(99999,1);
int count = 0;
char dirhold[100];
char* dot;
int line_num = 1;
int find_result=0;
static int check=0;
DIR* fdp;
if(dp==NULL)
{
printf("cant open");
}
struct stat filestat;
while (file=readdir(dp)) {
stat(file->d_name,&filestat);
//printf("%s\n",file->d_name);
if(strcmp(file->d_name,"..")==0||(strcmp(file->d_name,".")==0))
continue;
if ( S_ISDIR(filestat.st_mode))
{
check++;
printf("\n CHECK: %d\n",check);
if(check!=1)
{
strcpy(temppath,directoryname);
printf("temp:%s\n",directoryname);
strcat(directoryname,"\\");
strcat(directoryname,file->d_name);
dot = strrchr(directoryname, '\\');
strcpy(dirhold,dot);
printf("DIR: %s\n",dirhold);
printf("%s KLASÖR\n",directoryname);
chdir(directoryname);
dp=opendir(directoryname);
if(dp==NULL)
{
printf("2\n");
}
strcat(temppath,dirhold);
searchDirectoryForString(dp,str,temppath);
}
else{
strcpy(path,directoryname);
strcat(directoryname,"\\");
strcat(directoryname,file->d_name);
dot = strrchr(directoryname, '\\');
strcpy(dirhold,dot);
printf("DIR: %s\n",dirhold);
printf("%s FOLDER\n",directoryname);
chdir(directoryname);
dp=opendir(directoryname);
if(dp==NULL)
{
printf("2\n");
}
strcat(path,dirhold);
searchDirectoryForString(dp,str,path);
}
}
else
{
snprintf(buf,99999,"%s",directoryname);
strcat(buf,"\\");
//strcat(directoryname,dirhold);
strcat(buf,file->d_name);
printf("%s FILE \n",buf);
//Close the file if still open.
if(fp) {
fclose(fp);
strcpy(rpath," ");
}
}
}
}
}
I tried my best to produce an MCVE below. One new thing i noticed is if i add another folder to a preset folder it is not recognized as a folder. If i add a folder to a folder which is one of the recognized folders it is also recognized as a folder(nested folder) the issue is most likely caused by the stat function so wrote another function for it to be able to seen more clearly as its in its own scope. Im using devc++ as my IDE.
#include <locale.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>
#include <sys/stat.h>
#include <wchar.h>
int main(int argc, char *argv[]) {
setlocale(LC_ALL,"Turkish");
DIR* dp;
struct dirent dirp;
char test[9999];
getcwd(test,9999);
printf("FOLDERMAIN:%s\n",test);
dp=opendir(".");
listDir(dp,test);
return 0;
}
int isDir(const char *name) {
struct stat fileStat;
return !stat(name, &fileStat) && S_ISDIR(fileStat.st_mode);
}
void listDir(DIR* dp,char* directoryname)
{
struct dirent *file;
char buf[99999];
char* temppath;
temppath = calloc(99999,1);
int count = 0;
char dirhold[100];
char* dot;
int line_num = 1;
int find_result=0;
static int check=0;
char* dir;
DIR* fdp;
if(dp==NULL)
{
printf("cant open");
}
while (file=readdir(dp)) {
//printf("%s\n",file->d_name);
if(strcmp(file->d_name,"..")==0||(strcmp(file->d_name,".")==0))
continue;
if (isDir(file->d_name))
{
check++;
printf("\n CHECK: %d\n",check);
strcpy(temppath,directoryname);
printf("temp:%s\n",directoryname);
strcat(directoryname,"\\");
strcat(directoryname,file->d_name);
dot = strrchr(directoryname, '\\');
strcpy(dirhold,dot);
printf("DIR: %s\n",dirhold);
printf("%s FOLDER\n",directoryname);
chdir(directoryname);
dp=opendir(directoryname);
if(dp==NULL)
{
printf("2\n");
}
strcat(temppath,dirhold);
listDir(dp,temppath);
}
else
{
snprintf(buf,99999,"%s",directoryname);
strcat(buf,"\\");
strcat(buf,file->d_name);
printf("%s FILE\n",buf);
}
}
}
This is my code that does more or less what you want. It is also available in my SOQ (Stack Overflow Questions) repository on GitHub as file dirlist43.c in the src/so-7406-2431 sub-directory.
What went wrong in the MCVE code?
There were a fairly large number of problems, including:
Not much error checking for failed calls.
Changing directory with changing back correctly.
No debug code to help determine what is going wrong.
Too many places where opendir() was called.
Too many places where the result of opendir() was checked — one of them in a different function from where the call was made.
Missing headers.
Unused variables.
Not reporting errors on stderr.
Confusion about directoryname vs temppath.
Sequencing of chdir() calls worked downwards, once. But there was no code to change back after finishing a directory.
Missing closedir() — I ran out of file descriptors when run on a directory with about 3000 sub-folders.
More debatable: I've used snprintf() or printf() to concatenate strings. For raw performance, you may find that strcpy() and strcat() are quicker, but using strcat() can slow things down as it scans from the start of the string each time. Clever code determines the length of the string so that you can copy each time you append, but the Standard C functions aren't helpful for that.
And notes from another comment:
Note that using chdir() is error prone — doubly so if there are symlinks lurking around — and should be avoided when possible. (This was an accurate comment.)
You have a large number of occurrences of 99999 (and some occurrences of 9999); there should be a macro (or enumeration constant) for that value.
On Unix systems, you must #include <errno.h> and should never declare extern int errno; because errno is typically not a simple variable but rather an expression that evaluates to a per-thread int value. I think the same is true on Windows.
Other solutions
In a comment, I noted that:
If you were coding on a Unix (POSIX) system, it would be an odds-on bet that the problem and the fixes are described in stat() error "no such file or directory when file name is returned by readdir(). Even on Windows, you are likely to be running into similar problems. However, there are Windows-specific APIs to process directories that could be used instead of readdir().
Since your MCVE code uses Windows-style \ path separators but compiles, the majority of the advice in that question is valid. Note that POSIX provides some functions — fstatat() and dirfd() — that avoid messing around with chdir(). This is advantageous, especially in threaded programs as chdir() changes directory for all threads at the same time, which is apt to cause chaos if the different threads are accessing files in different directories identified by relative rather than absolute pathnames.
Code: dirlist43.c
#include <dirent.h>
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#ifdef UNIX
#define DIRSEP_STR "/"
#define DIRSEP_CHR '/'
#else
#define DIRSEP_STR "\\"
#define DIRSEP_CHR '\\'
#endif
#define PATH_LEN 99999
extern void listDir(const char *directoryname);
extern int isDir(const char *name);
int main(void)
{
setlocale(LC_ALL, "Turkish");
char pwd[PATH_LEN];
getcwd(pwd, sizeof(pwd));
printf("FOLDERMAIN: %s\n", pwd);
listDir(pwd);
return 0;
}
int isDir(const char *name)
{
struct stat fileStat;
int rc = stat(name, &fileStat) == 0 && S_ISDIR(fileStat.st_mode);
#ifdef DEBUG
printf("%s(): %d [%s]\n", __func__, rc, name);
#endif /* DEBUG */
return rc;
}
#ifdef DEBUG
static void chk_pwd(const char *tag)
{
char pwd[PATH_LEN];
if (getcwd(pwd, sizeof(pwd)) == 0)
{
int errnum = errno;
fprintf(stderr, "%s(): getcwd() failed: %d %s\n",
__func__, errnum, strerror(errnum));
exit(1);
}
printf("PWD: %s [%s]\n", tag, pwd);
}
#else
#define chk_pwd(tag) ((void)0)
#endif /* DEBUG */
static void set_pwd(const char *directoryname)
{
#ifdef DEBUG
printf("CHDIR 1: [%s]\n", directoryname);
#endif /* DEBUG */
if (chdir(directoryname) != 0)
{
int errnum = errno;
fprintf(stderr, "%s(): chdir(%s) failed: %d %s\n",
__func__, directoryname, errnum, strerror(errnum));
exit(1);
}
chk_pwd("CHDIR 1");
}
void listDir(const char *directoryname)
{
#ifdef DEBUG
static int level = 0;
#endif /* DEBUG */
static int check = 0;
#ifdef DEBUG
printf("-->> %s() level %d (%s)\n", __func__, ++level, directoryname);
#endif /* DEBUG */
DIR *dp = opendir(directoryname);
if (dp == NULL)
{
perror(directoryname);
return;
}
set_pwd(directoryname);
struct dirent *file;
while ((file = readdir(dp)) != NULL)
{
if (strcmp(file->d_name, "..") == 0 || (strcmp(file->d_name, ".") == 0))
continue;
if (isDir(file->d_name))
{
printf("CHECK: %d\n", ++check);
char temppath[PATH_LEN];
snprintf(temppath, sizeof(temppath), "%s%s%s",
directoryname, DIRSEP_STR, file->d_name);
printf("FOLDER: %s\n", temppath);
chk_pwd("Before recursion");
listDir(temppath);
set_pwd(directoryname);
chk_pwd("After recursion");
}
else
{
printf("FILE: %s%s%s\n", directoryname, DIRSEP_STR, file->d_name);
}
}
closedir(dp);
#ifdef DEBUG
printf("<<-- %s() level %d (%s)\n", __func__, level--, directoryname);
#endif /* DEBUG */
}
It can be compiled with -DUNIX if you work on a POSIX-like system where slash / is used to separate path components, or without if you work on a Windows-like system where backslash \ is used. It can be compiled with -DDEBUG to get copious extra debugging output.
The makefile is configured for Unix — the code was tested on a MacBook Pro running macOS Big Sur 11.7.
I have a header file named store.h which contains a struct and my main is located in E.c,i am currently experiencing an error which i can not comprehend why it is happening. Any help is much appreciated
store.h:
struct st{
char *buf,*var;
int line_number;
char *keywords;
char *operators;
};
E.C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "store.h"
#define max 1000000
int main(int argc,char *argv[]){
FILE *fp;
int ag=0;
fp=fopen( argv[1],"r");
if(fp==NULL){
printf("error cannot open file\n");
exit(1);
}
while(feof(fp)==0){
*buf=fgets(*buf,max,fp);
// irrelevant commands follow
}
fclose(fp);
}
On the line *buf=fgets(*buf,max,fp); I get the error:‘buf’ undeclared (first use in this function), however I have declared it in my struct and I have included the file that contains the struct, where am I wrong?
buf is a member of the struct.
You need to define the struct object.
You need to allocate memory for buf.
You should not assign buf with return value as it may cause a memory leak
Why is “while ( !feof (file) )” always wrong?
int main(int argc,char *argv[]){
FILE *fp;
struct st s;
int ag=0;
s.buf = malloc(max);
if(!s.buf) exit(2);
fp=fopen( argv[1],"r");
if(fp==NULL){
printf("error cannot open file\n");
exit(1);
}
while(fgets(s.buf,max,fp)){
/* ...*/
}
/* .... */
I have to read a text file using this structure. Also, I have to use external functions. I made the code for file reading and it works in main function.
Text file:
banana 3 orange 8 music 9- first character is a blank space*
#include <stdio.h>
#include <stdlib.h>
struct file
{
char name[30];
char size;
};
int main()
{
int n=0;
struct file f[30];
FILE *files;
files=fopen("files.txt","r");
int n=0;
while (1)
{
fgetc(files);
if(feof(files)) break;
fscanf(files,"%s %c",&f[n].name,&f[n].size);
n++;
}
}
But when I try to make this reading using another c file and extern function it's no working.. :(
This is what is written in filereading.c:
void fileReading(struct file *f[30], FILE *files)
{
int n=0;
while (1)
{
fgetc(files);
if(feof(files)) break;
fscanf(files,"%s %c",&f[n].name,&f[n].size);
n++;
}
}
And fileReading.h:
void fileReading(struct fisier *, FILE *);
And in main.c:
#include <stdio.h>
#include <stdlib.h>
struct file
{
char name[30];
char size;
};
int main()
{
int n=0;
struct file f[30];
FILE *files;
files=fopen("files.txt","r");
fileReading(f[30],files);
}
When I compile it, it says:
request for member 'name' in something not a structure or union
request for member 'size' in something not a structure or union|
||=== Build finished: 2 errors, 2 warnings (0 minutes, 0 seconds) ===||
Can you help me, please? Thank you!
From what I see it looks like you do not have a good understanding of pointers.
These changes should solve your problem:
void fileReading(struct file *f, FILE *files)
{
int n=0;
while (1)
{
fgetc(files);
if(feof(files)) break;
fscanf(files,"%s %c",f[n].name,&f[n].size);
//printf("%s %c",f[n].name,f[n].size);
n++;
}
}
int main()
{
int n=0;
struct file f[30];
FILE *files;
files=fopen("files.txt","r");
fileReading(f,files);
}
What you did wrong:
void fileReading(struct file *f[30], FILE *files) //here you were saying file is a **
fscanf(files,"%s %c",&f[n].name,&f[n].size); // here you need to send the a char* but you were sending a char ** as a second parameter
fileReading(f[30],files); // here you were sending the 31th element of the structure array f which by the way doesn't exist (indexing is from 0 , f[29] is the last) even though that was not what you wanted to do in the first place
The file fileReading.c doesn't know the definition of struct file. You need to move it from main.c to fileReading.h and #include "fileReading.h" in both main.c and fileReading.c.
Also, the definition and call of fileReading is incorrect. Instead of:
void fileReading(struct file *f[30], FILE *files)
You want:
void fileReading(struct file *f, FILE *files)
And you call it like this:
fileReading(f,files);
This is incorrect:
fileReading(f[30],files);
Because you're passing a single struct file instead of the array, and the single instance you're passing is one element off the end of the array (since the size is 30, valid indexes are 0-29), which can cause indefined behavior.
Using C on Linux, I'm writing a code that stores all the information about the files in a directory using function stat() and prints them on the Terminal
The algorithm is quite simple, I made a structure array of "files" and dynamically allocated them. The structure contains a char array (string) so I dynamically allocated it too.
The thing is .. the dynamic allocation works fine but if I'm inside the while loop I can access the other element inside the structure - which is a structure stat object - but if I access it after the loop finishes, it gives me "Segmentation Fault"!
Here's the code
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h>
struct file{
char* name;
struct stat fbuf;
};
int main(int argc, char **argv)
{
char* dir=NULL;
int k;
dir=(char *)malloc(strlen(argv[argc-1])+1);
dir=argv[argc-1];
strcpy(dir,argv[argc-1]);
DIR *curr_dir;
struct dirent *dir_inode;
int i,j=0;
char* sum=NULL;
struct file* files=NULL;
if ((curr_dir = opendir(dir)) == NULL) {
fprintf(stderr, "Can't Open %s\n", argv[1]);
exit(2);
}
while (((dir_inode = readdir(curr_dir))) != NULL) {
files=(struct file*) realloc(files,((j)+1)*(sizeof(char*)+sizeof(struct stat))); // Structure array reallocation
(files+(j))->name=(char *)(malloc(strlen(dir_inode->d_name)+1));//name allocation
for(i=0;i<strlen(dir_inode->d_name);i++)
(files+(j))->name[i]=dir_inode->d_name[i];//name storage
(files+(j))->name[i]='\0';
sum= (char *) malloc(strlen(dir)+strlen(dir_inode->d_name)+2);//To add file name to its directory
for(i=0;i<strlen(dir);i++)
sum[i]=dir[i];
sum[i]='/';
i++;
for(k=0;dir_inode->d_name[k]!='\0';k++)
sum[i+k]=dir_inode->d_name[k];
sum[i+k]='\0';//file name with directory in sum
if( stat(sum,&((files+j)->fbuf)) == -1){ // the function gets information from the file name and stores them in fbuf
printf("error stat\n");
exit(1);
}
free(sum);
if( S_ISDIR( ( (files+(j))->fbuf ).st_mode ) ){
printf("d");
}
else {
printf("-");
}
//Here the output appears fine
//The output depends on accessing fbuf in files array
printf("statOK\n");
(j)++; // index
}
printf("%d %d %d\n",files,j,files+1);
printf("%d\n",j);
printf("\n\n\n\n");
for(i=0;i<j;i++){
printf("%s\n",(files+i)->name);
printf("%d\n",files);
//Starting from here, same syntax but outside the loop it gives the error
if( S_ISDIR( ( (files+i)->fbuf ).st_mode ) ){
printf("d");
else {
printf("-");
}
}
free(files);
free(dir);
closedir(curr_dir);
exit(1);
}
The code isn't complete yet but all what I want is to access the fbuf outside the loop, then I can complete it
Any ideas?
Bad size assumption
This allocation is wrong:
files=(struct file*) realloc(files,((j)+1)*(sizeof(char*)+sizeof(struct stat)));
Here, you assumed that the size of struct file was the sum of the sizes of its two components. But in fact, you don't know how that structure is packed and aligned, so the size of struct file could be larger than what you thought. You should just be using sizeof(struct file) instead:
files=(struct file*) realloc(files,(j+1)*(sizeof(struct file)));
I got another warning which is
C4047: 'initializing' : 'char *' differs in levels of indirection from 'int'.
Why do I get those warnings? I have read on MSDN that it generated if I wrote a different function name to that I wrote in the header file or it should be extern function.
Could someone shed some light on it?
My relevant code is:
GetSignature.c
#include "Parse_Database.h"
#include "Alloc_Mem.h"
#include "Get_Signature.h"
int Get_Signature(const char *filename) {
char *database = AllocMem(filename);
int error=0;
//ParseDatabase(database);
return 1;
}
AllocMem
#include "Alloc_Mem.h"
char *AllocMem(const char *filename) {
FILE *fp = NULL;
int ch = EOF;
char *buf = NULL, *tmp = NULL;
unsigned int size=0, index=0;
/*
Mallicous use of fopen(anyone could replace filename with a malicous modified file)
*/
fp = fopen(filename, "r");
if(fp == NULL) {
fprintf(stderr, "Can't open the signature database");
exit(1);
}
while(ch) {
fread(&ch, 1, 1, fp);
if(ch == EOF) {
ch = 0;
}
if(size <= index) {
size+=chunk;
tmp = realloc(buf, size);
if(!tmp) {
free(buf);
buf = NULL;
break;
}
buf = tmp;
}
buf[index++] = ch;
}
fclose(fp);
return buf;
}
Alloc_Mem.h
#ifndef HEADERFILE_H
#define HEADERFILE_H
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#define chunk 255
#include <stdio.h>
#include <stdlib.h>
char *AllocMem(const char *);
#endif
I guess this shall resolve your problem: replace these lines:
#ifndef HEADERFILE_H
#define HEADERFILE_H
with
#ifndef _ALLOC_MEM_H
#define _ALLOC_MEM_H
Reason: header guards shall be unique for each header file, or there would be corruption when including multiple header files. (And I guess you're using the same name in all your header files.)
EDITED: Also in other of your headers (Parse_Database.h and Get_Signature.h) you shall replace the header guards with different names.
It would be helpful to know the exact line where the error is happening. I am assuming it is
char *database = AllocMem(filename);
in GetSignature.c.
Generally, this error comes about because the C compiler does not have the declaration of a function when it is called (what function that is depends upon the line mentioned in the error message). When this occurs, the C compiler assumes that the function returns int.
Do you have the include guard:
#ifndef HEADERFILE_H
#define HEADERFILE_H
In Parse_Database.h as well? If so, it is blocking the declaration of AllocMem from being included because HEADERFILE_H is already defined.
The usual custom is to name the defined macro the same as the header file. For example:
#ifndef ALLOC_MEM_H
#define ALLOC_MEM_H