views:

767

answers:

4

Ultimately I want to travel through a folder's files and subdirectories and write something to all files i find that have a certain extension(.wav in my case). when looping how do i tell if the item I am at is a directory?

A: 

opendir and readdir (on unix), here's an example:

http://opengroup.org/onlinepubs/007908775/xsh/readdir.html

or FindFirstFile on windows

you could also use the shell pretty easily:

find . -name "*.wav"

or

ls **/*.wav    (in zsh and newer bashes)
jspcal
oops, forgot to say I'm doing this on windoze also.
+3  A: 

Based on your mention of .wav, I'm going to guess you're writing code for Windows (that seems to be where *.wav files are most common). In this case, you use FindFirstFile and FindNextFile to traverse directories. These use a WIN32_FIND_DATA structure, which has a member dwFileAttributes that contains flags telling the attributes of the file. If dwAttributes & FILE_ATTRIBUTE_DIRECTORY is non-zero, you have the name of a directory.

Jerry Coffin
Yeah, I'm doing this on Windows. My C is really rusty, haven't programmed in C for a few years. I was trying to use dirent.h, but I dont think it offers any way of telling if the item is a directory or not. Can't find a good example of FindFirstFile from google
This page shows how to use them (in VB, but translation to C should be more or less straight forward): http://support.microsoft.com/kb/185476. Note that you might also want to think about how you hande NTFS junctions, i.e. whether to follow them or not, and if you do how to avoid endless loops.
Carsten
+1  A: 

Here is how you do it (this is all from memory so there may be errors):

void FindFilesRecursively(LPCTSTR lpFolder, LPCTSTR lpFilePattern)
{
    TCHAR szFullPattern[MAX_PATH];
    WIN32_FIND_DATA FindFileData;
    HANDLE hFindFile;
    // first we are going to process any subdirectories
    PathCombine(szFullPattern, lpFolder, _T("*"));
    hFindFile = FindFirstFile(szFullPattern, &FindFileData);
    if(hFindFile != INVALID_HANDLE_VALUE)
    {
        do
        {
            if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                // found a subdirectory; recurse into it
                PathCombine(szFullPattern, lpFolder, FindFileData.cFileName);
                FindFilesRecursively(szFullPattern, lpPattern);
            }
        } while(FindNextFile(hFindFile, &FindFileData));
        FindClose(hFindFile);
    }
    // now we are going to look for the matching files
    PathCombine(szFullPattern, lpFolder, lpFilePattern);
    hFindFile = FindFirstFile(szFullPattern, &FindFileData);
    if(hFindFile != INVALID_HANDLE_VALUE)
    {
        do
        {
            if(!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            {
                // found a file; do something with it
                PathCombine(szFullPattern, lpFolder, FindFileData.cFileName);
                _tprintf_s(_T("%s\n"), szFullPattern);
            }
        } while(FindNextFile(hFindFile, &FindFileData));
        FindClose(hFindFile);
    }
}

So you could call this like

FindFilesRecursively(_T("C:\\WINDOWS"), _T("*.wav"));

to find all the *.wav files in C:\WINDOWS and its subdirectories.

Technically you don't have to do two FindFirstFile() calls, but I find the pattern matching functions Microsoft provides (i.e. PathMatchFileSpec or whatever) aren't as capable as FindFirstFile(). Though for "*.wav" it would probably be fine.

Luke