PDA

View Full Version : getting a list of files from a directory




farmerdoug
Apr 7, 2013, 11:50 AM
I am writing a routine to return a list of files in a directory in C. The routine takes the directory name and a maximum value for the number of files. I want it to be portable. And I'd like to get rid of the warning that **file_list is not initialized. Comments and help. thanks.


#include <stdio.h>
#include <stdlib.h>
#define num_files 100
void get_file_list( char * dir, char ** file_list);
int main(int argc, const char * argv[])
{

char **file_list, *dir;

dir = (char *) calloc(25, sizeof(char));
file_list = (char **) calloc(25, sizeof(char*));

get_file_list(dir, file_list);


return 0;
}


void get_file_list( char * dir, char ** file_list)
{

int i;
file_list = (char **) calloc(num_files, sizeof(char*));
for (i = 0; i < num_files; i++)
file_list[i] = (char *) calloc(25, sizeof(char));

//code


}



lloyddean
Apr 7, 2013, 08:17 PM
Are you asking for code to read a directory?

I'm sure you'll find plenty of examples along the lines of ...


void listdir(char* dir)
{
struct dirent* pdirent;
DIR* pdir = opendir(dir);
if ( pdir )
{
while ((pdirent = readdir(pdir)) != NULL)
{
if ( ! strcmp(pdirent->d_name, ".") || ! strcmp(pdirent->d_name, "..") )
{
/* don't bother showing parent or current directory */
continue;
}

printf("%s/%s\n", dir, pdirent->d_name);
}

closedir(pdir);

return;
}

fprintf(stderr, "listdir: can't open %s\n", dir);
}


... that you can adapt to your purposes.

farmerdoug
Apr 7, 2013, 08:35 PM
Actually, I was more interested in how I should allocate memory and pass pointers back and forth. The code you sent, which is actually very helpful because it teaches me about calls I was aware of, doesn't help me see how best to that. I need a list of files in the directory.

lloyddean
Apr 7, 2013, 10:25 PM
Well without much thought and the restriction that the directory being traversed is static and will not change during the running of the program -


int get_file_list(char* pszDirectoryName, char** ppsz)
{
int entries_count = 0;

struct dirent* pdirent;
DIR* pdir;

pdir = opendir(pszDirectoryName);
if ( pdir )
{
while ( (pdirent = readdir(pdir)) )
{
if ( ! strcmp(pdirent->d_name, ".") || ! strcmp(pdirent->d_name, "..") )
{
/* don't bother show parent or current directory */
continue;
}

if ( ppsz )
{
char* psz = (char*)calloc(strlen(pdirent->d_name) + 1, sizeof(char));
strcpy(psz, pdirent->d_name);

ppsz[entries_count] = psz;
}

entries_count++;
}

closedir(pdir);

return entries_count;
}

return 0;
}

int main(void)
{
int entries_count = get_file_list("/", NULL);
char** ppsz = (char**)calloc(entries_count, sizeof(char*));
entries_count = get_file_list("/", ppsz);

for (int i = entries_count; i--; )
{
printf("%s\n", ppsz[i]);

free((void*)ppsz[i]);
}

free((void*)ppsz);

return EXIT_SUCCESS;
}

robvas
Apr 8, 2013, 07:18 AM
Doug:

I'm going to suggest again you use a language like Python or Ruby for your tasks. You can do something like this in a one-liner:

dir_contents = Dir.entries("/absolute/path/to/directory")

Senor Cuete
Apr 8, 2013, 10:19 AM
You need to balance your calls to calloc() with free() or you will have memory leaks.

farmerdoug
Apr 8, 2013, 10:48 AM
Leaks.
In the main code, if I call the routine more than once. Yes. Thanks.

lloyddean
Apr 8, 2013, 11:07 AM
You need to balance your calls to calloc() with free() or you will have memory leaks.

As I said I threw it together without much thought.

As well you're presuming my motive is to provide Doug with well written software and I can assure you it's not. I'm simply trying to get him to "Think Different" not do his work for him.

farmerdoug
Apr 8, 2013, 11:19 AM
Lloyd,
I've really been trying to clean up my act. That's why the orignal question was more about passing pointers in the proper manner than writing the read directory code, as I said.

lloyddean
Apr 8, 2013, 01:13 PM
The same thing done in C++ mixed with C.

Note the use of STL containers 'string' and 'vector' handling all memory allocation/deallocation for you!

Again threw it together with little thought other than showing the utility of C++ and the STL to simplify programming tasks such as yours.


#include <cstdlib>

#include <iostream>
#include <string>
#include <vector>

#include <dirent.h>

typedef std::vector<std::string> svec_t;
typedef std::vector<std::string>::iterator svec_itr;

void GetDirContnets(const char* pszDirectoryName, svec_t& svec)
{
DIR* pdir = opendir(pszDirectoryName);
if ( pdir )
{
struct dirent* pdirent;

while ( (pdirent = readdir(pdir)) )
{
if ( strcmp(pdirent->d_name, ".") && strcmp(pdirent->d_name, "..") )
{
svec.push_back(pdirent->d_name);
}
}

closedir(pdir);
}
}

int main(void)
{
svec_t svec;
GetDirContnets("/", svec);

for ( svec_itr itr = svec.begin(); itr != svec.end(); itr++ )
{
cout << *itr << "\n";
}

return EXIT_SUCCESS;
}

subsonix
Apr 8, 2013, 02:37 PM
Assuming the question is about resource management. It would probably be better to do all allocation in your get_* function as opposed to doing it in two places. Just return a new list or NULL on failure. Also, since you are allocating for each directory entry string manually, there is no need to restrict yourself to 25 characters, allocate strlen +1 or use strdup.

Finally create a corresponding free_* function to free the entire list in one go.

lloyddean
Apr 8, 2013, 03:02 PM
I'm simply trying to get him to reconsider his contortionist memory allocation and parameter passing monstrosities as being complex, mind numbing and totally unnecessary.

Senor Cuete
Apr 8, 2013, 05:03 PM
Leaks.
In the main code, if I call the routine more than once. Yes. Thanks.

Even once.