0

I need to make some operations on a list of files with particular extension (*.bob), all stored in the same directory. The files are image frames, and their name format is frame_XXXX.bob. I don't know the number of frames a priori, and I need to make sure I process them in order (from frame 0 to last one). I read the content of the folder with struct dirent *readdir(DIR *dirp), but since it doesn't guarantee files will be read in alphabetical order (even though it always seems to), I want to put them into a single linked list, and then sort them before processing further. I save the head of the list before populating it to a pointer filesListStart, then read the entire folder content, adding each entry to the list if it has ".bob" extension. This all works great when I have up to 100 frames, but for some reason breaks down above that - the value of what pointer filesListStart points at doesn't contain the filename of the first entry in the list anymore. The code doesn't use any numerals, so I don't know what would be the significance of going over 100 elements. I wrote out memory address of filesListStart before I start populating the list, and after, and they are the same, but values they show at magically change. When I set filesListStart it points at object with field fileName equals to "frame_0000.bob" (which is as expected), but after populating the list the name it points at becomes "e_0102.bob".

The list structure is defined as

// List structure
struct FilesList {
  char *fileName ;
  struct FilesList *next ;
} ;

The code in question is:

DIR *moviesDir ;
moviesDir = opendir("movies") ;

if(moviesDir == NULL)
{
  printf("Make Movie failed to open directory containing bob frames\n") ;
  return ;
}

struct dirent *dirContent ;

// Get first .bob frame name from the directory
dirContent = readdir(moviesDir) ;
// isBobFile(dirContent) returns 1 if entry has ".bob" extension and 0 otherwise
while( !isBobFile(dirContent) )
{
  dirContent = readdir(moviesDir) ;
}

struct FilesList *filesList = (struct FilesList*)
    malloc( sizeof(struct FilesList) ) ;

// Initialize the list start at that first found .bob frame
filesList->fileName = dirContent->d_name;
// And save the head of the list
struct FilesList *filesListStart = filesList ;
printf("FilesListStart: %s\n", filesListStart->fileName) ;
printf("Address is: %p\n", filesListStart) ;

// For all other bob frames
while( (dirContent = readdir(moviesDir) ) != NULL )
{
  if( isBobFile(dirContent) )
  {
    struct FilesList *temporaryNode = (struct FilesList*)
        malloc( sizeof(struct FilesList) );
    temporaryNode->fileName = dirContent->d_name ;
    filesList->next = temporaryNode ;
    filesList = temporaryNode ;
  }
}
// Set the 'next' pointer of the last element in list to NULL
filesList->next = NULL ;
// close stream to directory with .bob frames
closedir(moviesDir) ;

// Check what FilesListStart points at
printf("FilesListStart: %s\n", filesListStart->fileName) ;
printf("Address is: %p\n", filesListStart) ;

// Rest of the code
Puchatek
  • 1,477
  • 3
  • 15
  • 33

1 Answers1

2

You should be making a copy of dirContent->d_name rather than using the actual value.

The runtime libraries are free to change the contents of that dirent structure whenever you call readdir and, if all you've stored is it's address, the underlying memory may change. From the POSIX man-pages:

The pointer returned by readdir() points to data which may be overwritten by another call to readdir() on the same directory stream.

In other words, replace the lines:

filesList->fileName = dirContent->d_name;
temporaryNode->fileName = dirContent->d_name ;

with:

filesList->fileName = strdup (dirContent->d_name);
temporaryNode->fileName = strdup (dirContent->d_name);

assuming you have a strdup-like function and, if not, you can get one cheap here.

If it's only changing after 100 calls, it's probably the runtime trying to be a bit more intelligent but even it can't store an infinite number so it probably sets a reasonable limit.

Just remember to free all those char pointers before you free the linked list nodes (somewhere in that "rest of code" section presumably).

Community
  • 1
  • 1
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Worked like a charm, thank you. I suspected the problem was with `filesList->fileName = dirContent->d_name` as you pointed out, but couldn't figure out how to copy a value from one pointer to another. Any `*(filesList->fileName) = *(dirContent->d_name)` and other contraptions I came up with caused either warnings, segmentation faults, or illegal instructions. Thank you once again. – Puchatek Aug 01 '12 at 06:18
  • @Puchatek: make sure you change the other line as well (which I originally missed) - I've updated the answer to show it. – paxdiablo Aug 01 '12 at 06:22