I'm using readdir() to read to output all the files in a directory. The problem is that I need to save the strings into a buffer. Is there any way to save the output into a buffer or file descriptor etc?
Here is my code:
DIR *directory;
struct dirent *dir;
directory = opendir();
while ((dir = readdir(directory)) != NULL) {
printf("%s\n", dir->d_name);
}
closedir(directory);
Use scandir How to use scandir
It allocates the memory for you. Here is an example
/**code to print all the files in the current directory **/
struct dirent **fileListTemp;
char *path = ".";//"." means current directory , you can use any directory path here
int noOfFiles = scandir(path, &fileListTemp, NULL, alphasort);
int i;
printf("total: %d files\n",noOfFiles);
for(i = 0; i < noOfFiles; i++){
printf("%s\n",fileListTemp[i]->d_name);
you can modify it to suit your need . Also do not forget to free memory allocated by scandir
In this case you will have to work with a data structure, like a linked list or a tree.
Here's a short example, an idea to work on:
pDir = readdir(directory);
while ( pDir != NULL ){
strcpy(dirStruct[iCtFile]->FileName, pp->d_name);
}
Looking ahead, you should consider path treatment and other issues.
Note that you'll need to pass a directory name to opendir().
This is simple example of how to read and save them into a buffer. It doubles the pointers every time it hits the limit. Modern operating systems will clean up the memory allocated once process dies. Ideally, you should also call free() in case of failures.
#include<stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include<string.h>
#include<stdlib.h>
int main(void)
{
size_t i = 0, j;
size_t size = 1;
char **names , **tmp;
DIR *directory;
struct dirent *dir;
names = malloc(size * sizeof *names); //Start with 1
directory = opendir(".");
if (!directory) { puts("opendir failed"); exit(1); }
while ((dir = readdir(directory)) != NULL) {
names[i]=strdup(dir->d_name);
if(!names[i]) { puts("strdup failed."); exit(1); }
i++;
if (i>=size) { // Double the number of pointers
tmp = realloc(names, size*2*sizeof *names );
if(!tmp) { puts("realloc failed."); exit(1); }
else { names = tmp; size*=2; }
}
}
for ( j=0 ; j<i; j++)
printf("Entry %zu: %s\n", j+1, names[j]);
closedir(directory);
}
Related
prev problem was not described well.
I need to retrieve a file name and assign that name to a variable. The only thing is that the file can have any name.
The thing that I know is the file extension and there can only be one file of this extension.
Is there any way to implement this in C?
Update #1
File extension is *.knb and target is Linux system.
#include <stdio.h>
#include <dirent.h>
#define LOG_() do{fprintf(stdout, " UNIT TESTING...\n");}while(0)
char* knbname;
void fName(void);
int main()
{
LOG_();
fName(void);
//do some more code
return 0;
}
void fName(void){
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d)
{
//find file with *.knb extension and place name into knbname
}
closedir(d);
}
I don't know how to find for a *.knb file in the fName() function and how to assign name to knbname. Any help will be appreciated
You are reinventing POSIX's glob() function. Here is an example of how to use it (see the flags in manpage for more options):
#include <glob.h>
int main(void)
{
glob_t glob_res = { 0 };
glob("*.knb", 0, NULL, &glob_res);
if (glob_res.gl_pathc == 0) puts("No match.");
else printf("First match is %s\n", glob_res.gl_pathv[0]);
globfree(&glob_res);
return 0;
}
After Andrew pointed out some major flaw in the previous design, i will make another attempt for a solution...
int hasExtension(char *filename, char *extension);
int main(void)
{
DIR *directory;
struct dirent *entry;
if(NULL == (directory = opendir(".")))
{
perror("Failed to open directory\n");
exit(1);
}
while( NULL != (entry = readdir(directory)) )
{
if(DT_REG == entry->d_type) { // Regualar files only
if(hasExtension(entry->d_name, ".knb"))
{
printf("Match - %s\n", entry->d_name);
} else {
printf("No match - %s\n", entry->d_name);
}
}
}
closedir(directory);
return 0;
}
int hasExtension(char *filename, char *extension)
{
size_t filename_len, extension_len;
if((NULL == filename) || (NULL ==extension))
return 0;
filename_len = strlen(filename);
extension_len = strlen(extension);
if(filename_len < extension_len)
return 0;
return !strcmp(filename + filename_len - extension_len, extension);
}
I'm trying to store file names in an array. The array is in a struct, and I want to store the names of files found in a directory in the array. However, the process that I'm using to store the names seems to be corrupting 2 or 3 of the names during the process. I think the problem is with the strdup keyword. Whenever I run my program either it reads in the program executable (which is in the directory above the directory I'm reading the files from), or weird symbols stored in the first few array locations. The following is part of my program where I'm trying to capture and store the file names, and a picture of the output results:
typedef struct{
char *filename;
}filename;
typedef struct Configs{
char file_data_path[50];
char event_log_path[50];
filename *fn_data;
}Configs;
typedef struct TestConfigs{
bool done;
int selection;
int attempts_counter;
Configs tConfig;
}TestConfigs;
void read_files(struct TestConfigs *setup);
int main(void) {
printf("Hello Test\n");
TestConfigs setup;
read_files(&setup);
system("pause");
return EXIT_SUCCESS;
}
void read_files(struct TestConfigs *setup)
{
setup->done = false;
setup->attempts_counter = 3;
char cwd[1024];
DIR *dir = NULL;
struct dirent *pent = NULL;
struct stat info;
int total_num_of_files = 0;
strcpy(setup->tConfig.file_data_path, "data/");
chdir(setup->tConfig.frame_data_path);
if((getcwd(cwd, sizeof(cwd))) != NULL)
{
printf("Current Directory: %s\n", cwd);
}
dir = opendir(cwd);
if(dir != NULL)
{
while((pent = readdir(dir)) != NULL)
{
if(stat(pent->d_name, &info))
{
printf("ERROR: stat%s: %s\n", pent->d_name, strerror(errno));
}
else
{
if(S_ISREG(info.st_mode))
{
if((strcmp(pent->d_name, ".cproject") == 0) || (strcmp(pent->d_name, ".project") == 0))
{
continue;
}
else
{
total_num_of_files++;
}
}
}
}
printf("# of files found: %d\n", total_num_of_files);
rewinddir(dir);
// SETUP ARRAY HERE!
setup->tConfig.fn_data = malloc(total_num_of_files);
total_num_of_files= 0;
printf("During Storage Process:\n");
while((pent = readdir(dir)) != NULL)
{
if(stat(pent->d_name, &info))
{
printf("ERROR: stat%s: %s\n", pent->d_name, strerror(errno));
}
else
{
if(S_ISREG(info.st_mode))
{
if((strcmp(pent->d_name, ".cproject") == 0) || (strcmp(pent->d_name, ".project") == 0))
{
continue;
}
else
{
setup->tConfig.fn_data[total_num_of_files].filename = (char*)malloc(sizeof(pent->d_name));
setup->tConfig.fn_data[total_num_of_files].filename = strdup(pent->d_name); // <- Possible source of the storage problem
printf("Filename stored in fn_data[%d] = %s\n", total_num_of_files, setup->Config.fn_data[total_num_of_files].filename);
total_num_of_files++;
}
}
}
}
printf("\n");
printf("After Storage Process:\n");
for(int i = 0; i < total_num_of_files; i++)
{
printf("Filename stored in fn_data[%d] = %s\n", i, setup->tConfig.fn_data[i].filename);
}
}
closedir(dir);
}
Output results here
What can I do to resolve the corrupt storage of the filenames in the first few array locations? How come only the first few locations aren't properly storing the filenames, but the other locations are Ok? Is the issue with strdup, and if so, what's a good alternative for capturing and storing the file names in the array? Thanks in advance!
setup->tConfig.fn_data = malloc(total_num_of_files);
One byte per file?
Need, e.g., setup->tConfig.fn_data = malloc(total_num_of_files * sizeof(filename));
This:
setup->tConfig.fn_data[total_num_of_files].filename = (char*)malloc(sizeof(pent->d_name));
setup->tConfig.fn_data[total_num_of_files].filename = strdup(pent->d_name); // <- Possible source of the storage problem
makes no sense; the strdup() will overwrite the pointer returned by malloc() and that memory will be forever lost ("leaked") which is bad.
You don't have to allocate memory for strdup(), it does that for you. It's basically:
char * strdup(const char *s)
{
const size_t sz = strlen(s) + 1;
char * const p = malloc(sz);
if (p != NULL)
memcpy(p, s, sz);
return p;
}
I am trying to read all .txt files which is in the directory named "dataset". All text files has name like 1.txt, 2.txt, 3.txt... And then saving the contents of the files to a structure named FILES.
I used the dirent.h library and readdir( ) function as I saw in some sources. But the file name which program read from directory does not return correctly. Here is my related code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
typedef struct FILES{
char **words;
int wordCount;
}FILES;
void readFiles();
FILES *files;
int fileCount;
int main(){
readFiles();
return 0;
}
void readFiles(){
FILE *file;
DIR *directory;
struct dirent *filesInDirectory;
int counter;
fileCount = 1;
files = (FILES *)malloc(sizeof(FILES));
directory = opendir("dataset");
if(directory == NULL){
printf("Warning: The directory name that is given in the code is not
valid ..!");
return;
}else{
while((filesInDirectory = readdir(directory)) != NULL){
printf("%s\n", filesInDirectory->d_name);
file = fopen(filesInDirectory->d_name, "r+");
if(file == NULL){
printf("Warning: The file named %s could not open ..!",
filesInDirectory->d_name);
return;
}
files[fileCount-1].wordCount = 1;
files[fileCount-1].words = (char **)malloc(files[fileCount-
1].wordCount * sizeof(char *));
counter = 0;
while(!feof(file)){
files[fileCount-1].words[counter] = (char *)malloc(20 *
sizeof(char));
fscanf(file, "%s", files[fileCount-1].words[counter]);
files[fileCount-1].wordCount++;
files[fileCount-1].words = (char **)realloc(files[fileCount-
1].words, files[fileCount-1].wordCount * sizeof(char *));
counter++;
}
fileCount++;
fclose(file);
}
}
}
The file name that I printed in here "printf("%s\n", filesInDirectory->d_name);" is ".". Where am I doing wrong?
THIS IS NOT AN ANSWER BUT TOO BIG FOR A COMMENT
Here are your problems:
1.
files = (FILES *)malloc(sizeof(FILES));
This is enough size of one FILES. This is will not be enough
After
while((filesInDirectory = readdir(directory)) != NULL){
Put something like
size_t len = strlen(filesInDirectory->d_name);
if (len < 5 || strcmp(filesInDirectory->d_name + len - 4, ".txt") != 0) {
continue;
}
This will check to ensure files end in .txt
Consider using fstat to ensure that the file is a text file
Remove the casts on malloc
Perhaps use realloc for files
while ... feof is bad - see link above
fscanf(file, "%s", - Buffer overruns is possible. Do something about this. Read the manual page
I want to be able to get the list of folders and file (if possible with sub folders) and return it (array for example)
So i have this script:
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); // I dont want to print, I want to add it to a array
}
closedir (dir);
} else {
/* could not open directory */
perror ("");
return EXIT_FAILURE;
}
So i can do:
array = getFolders('.');
now if I loop thru the array i can see all folders and files.
eg:
array[0] = 'file1.txt';
array[1] = 'folder1/';
array[2] = 'folder1/file2.txt';
array[3] = 'folder1/file3.txt';
array[4] = 'folder2/';
array[5] = 'folder3/';
array[6] = 'folder3/filez.txt';
etc...
You can allocate a list of pointers and store each entry by allocating memory for the directory entry:
char **getFolder()
{
char **list = malloc(4096* sizeof *list);
size_t i = 0;
....
while ((ent = readdir (dir)) != NULL) {
list[i]=strdup(ent->d_name);
i++;
if( i >=4096 ) { /* time to realloc */}
}
return list;
}
and from the caller, you can use receive it as:
char **list=getFolder();
If you are going to have too many entries than you can use realloc() to increase the size of the list inside the loop. If strdup is not available, it can be easily implemented by using malloc() and strlen() functions.
I have excluded error checking for simplicity. Ideally, malloc() ans strdup should be checked for errors.
use DIR command in popen, and malloc and realloc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#define INC 1024
char **getFolders(const char *path, /* out */ size_t *count){
char buff[1024];
snprintf(buff, sizeof buff, "dir /B /S /ON \"%s\"", path);
FILE *fp = popen(buff, "r");
size_t capacity = INC;
char **entry;
struct stat finfo;
if(NULL==(entry=malloc(capacity*sizeof(*entry)))){
perror("malloc");
exit(EXIT_FAILURE);
}
*count = 0;
while(fgets(buff, sizeof buff, fp)){
size_t len;
for(len = 0; buff[len]; ++len){
if(buff[len] == '\\')
buff[len] = '/';
}
buff[len-1] = '\0';
stat(buff, &finfo);
if(S_ISDIR(finfo.st_mode))
buff[len-1] = '/';
entry[*count] = strdup(buff);
if(++*count == capacity){
capacity += INC;
if(NULL==(entry=realloc(entry, capacity*sizeof(*entry)))){
perror("realloc");
exit(EXIT_FAILURE);
}
}
}
pclose(fp);
return entry;
}
int main(void){
size_t i, count = 0;
char **entry = getFolders("c:/src/", &count);
for(i = 0; i < count; ++i){
printf("%s\n", entry[i]);
free(entry[i]);
}
free(entry);
return 0;
}
I am currently working with a code example that initially is designed to take an argument, then search for that argument in the current directory, I've tried to make it search another directory (/dev/shm to exact) by replacing the "." with "/dev/shm" but the code turns up nothing when i search for something* (notice the wildcard). The wild card search works fine in the current directory so I do not think it is the wild card that is the problem, If someone could help me out though I would really appreciate it, thanks!
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
static void lookup(const char *arg)
{
DIR *dirp;
struct dirent *dp;
if ((dirp = opendir(".")) == NULL) {
perror("couldn't open '.'");
return;
}
do {
errno = 0;
if ((dp = readdir(dirp)) != NULL) {
if (strcmp(dp->d_name, arg) != 0)
continue;
(void) printf("found %s\n", arg);
(void) closedir(dirp);
return;
}
} while (dp != NULL);
if (errno != 0)
perror("error reading directory");
else
(void) printf("failed to find %s\n", arg);
(void) closedir(dirp);
return;
}
int main(int argc, char *argv[])
{
int i;
for (i = 1; i < argc; i++)
lookup(argv[i]);
return (0);
}
opendir doesn't handle wildcards. It expects a real directory path. I'm not sure what you mean when you say
wildcard search works in the current directory
If you mean it works in your shell, that's to be expected. The shell will first expand the wildcard and then perform the command you typed.
So how to solve this? Expand the wildcard yourself using glob before calling opendir.
Edit: sorry, I thought you were trying to match the wildcard in the directory name. It looks like you want to match directory contents using the wildcard. In that case simply replace
if (strcmp(dp->d_name, arg) != 0)
with
if (fnmatch(arg, dp->d_name, 0) != 0)
You could also use glob for this. It will actually replace the call to opendir and the loop. Here is an example for using glob:
#include <glob.h>
#include <stdio.h>
static void lookup(const char *root, const char *arg)
{
size_t n;
glob_t res;
char **p;
chdir(root);
glob(arg, 0, 0, &res);
n = res.gl_pathc;
if (n < 1) {
printf("failed to find %s\n", arg);
} else {
for (p = res.gl_pathv; n; p++, n--) {
printf("found %s\n", *p);
}
}
globfree(&res);
}
int main(int argc, char *argv[])
{
int i;
for (i = 2; i < argc; i++)
lookup(argv[1], argv[i]);
return (0);
}
I'm not sure what you are expecting. If your program is called lookup then if you call it in the 'current directory', where that directory holds files something.1, something.2 and something.3 like this:
lookup something*
the shell will expand it to
lookup something.1 something.2 something.3
and your program will see three command line args and will be able to find a match in the readdir loop.
If you change the opendir call to "/dev/shm" and call it from the original directory (the one that has something.[1-3]) then the shell will again expand the wildcard in the current directory. But unless the files something.1, something.2 and something.3 are also present in /dev/shm, the readdir loop will not see them.
Note that your lookup function is a bit odd. I would expect it to be more like this:
static int lookup(const char * dir, const char *arg)
{
DIR *dirp;
struct dirent *dp;
if ((dirp = opendir(dir)) == NULL) {
perror(dir);
return -1;
}
while ((dp = readdir(dirp)) != NULL) {
if (!strcmp(dp->d_name, arg)) {
break;
}
}
(void) closedir(dirp);
printf("%s %s\n", dp ? "found" : "failed to find", arg);
return 0;
}