#include "precomp.h"
#pragma hdrstop

#if SCAN_DEBUG
BOOL dprinton = FALSE;
#endif

//
// This flag indicates whether empty directories should be deleted.
//

BOOL DeleteEmptyDirectories = FALSE;
BOOL ContinueOnError = FALSE;
BOOL Quiet = FALSE;
BOOL ShowWriteableFiles = FALSE;

DWORD
NewFile (
    IN PVOID Context,
    IN PWCH Path,
    IN PSMALL_WIN32_FIND_DATAW ExistingFileData OPTIONAL,
    IN PWIN32_FIND_DATAW NewFileData,
    IN PVOID *FileUserData,
    IN PVOID *ParentDirectoryUserData
    )

/*++

Routine Description:

    Called when ScanDirectory finds a file.

Arguments:

    Context - User-supplied context. Not used by emptydirs.

    Path - Directory containing this file.

    ExistingFileData - Pointer to data describing previous found file with
        same name, if any.

    NewFileData - Pointer to data for this file.

    FileUserData - Pointer to user-controlled data field for the file.

    ParentDirectoryUserData - Pointer to user-controlled data field for the
        parent directory.

Return Value:

    DWORD - Indicates whether an error occurred.

--*/

{
    //
    // Increment the directory/file count for the parent. Set the file's
    // user data pointer to NULL, indicating that we don't need the
    // scan library to remember this file.
    //

    (*(DWORD *)ParentDirectoryUserData)++;
    *FileUserData = NULL;

    dprintf(( "  NF: File %ws\\%ws: parent count %d, file count %d\n",
                Path, NewFileData->cFileName,
                *(DWORD *)ParentDirectoryUserData, *FileUserData ));

    //
    // If we're supposed to show writeable files, check for that now.
    //

    if ( ShowWriteableFiles &&
         ((NewFileData->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) ) {
        printf( "FILE: %ws\\%ws\n", Path, NewFileData->cFileName );
    }

    return 0;

} // NewFile

DWORD
NewDirectory (
    IN PVOID Context,
    IN PWCH Path,
    IN PSMALL_WIN32_FIND_DATAW ExistingDirectoryData OPTIONAL,
    IN PWIN32_FIND_DATAW NewDirectoryData,
    IN PVOID *DirectoryUserData,
    IN PVOID *ParentDirectoryUserData
    )

/*++

Routine Description:

    Called when ScanDirectory finds a directory.

Arguments:

    Context - User-supplied context. Not used by emptydirs.

    Path - Directory containing this directory.

    ExistingDirectoryData - Pointer to data describing previous found directory with
        same name, if any.

    NewDirectoryData - Pointer to data for this directory.

    DirectoryUserData - Pointer to user-controlled data field for the directory.

    ParentDirectoryUserData - Pointer to user-controlled data field for the
        parent directory.

Return Value:

    DWORD - Indicates whether an error occurred.

--*/

{
    //
    // Increment the directory/file count for the parent. Set the directory's
    // user data pointer to 1, indicating that the scan library should
    // remember this directory and scan it.
    //

    (*(DWORD *)ParentDirectoryUserData)++;
    *(DWORD *)DirectoryUserData = 1;

    dprintf(( "  ND: Dir  %ws\\%ws: parent count %d, dir  count %d\n",
                Path, NewDirectoryData->cFileName,
                *(DWORD *)ParentDirectoryUserData, *DirectoryUserData ));

    return 0;

} // NewDirectory

DWORD
CheckDirectory (
    IN PVOID Context,
    IN PWCH Path,
    IN PSMALL_WIN32_FIND_DATAW DirectoryData,
    IN PVOID *DirectoryUserData,
    IN PVOID *ParentDirectoryUserData
    )

/*++

Routine Description:

    Called when ScanDirectory has completed the recursive scan for a directory.

Arguments:

    Context - User-supplied context. Not used by emptydirs.

    Path - Path to this directory. (Not to containing directory.)

    DirectoryData - Pointer to data for this directory.

    DirectoryUserData - Pointer to user-controlled data field for the directory.

    ParentDirectoryUserData - Pointer to user-controlled data field for the
        parent directory.

Return Value:

    DWORD - Indicates whether an error occurred.

--*/

{
    BOOL ok;
    DWORD error;

    //
    // If the directory's directory/file count is 1, then the directory
    // contains no files or directories (the count is biased by 1), and
    // is empty.
    //

    if ( *(DWORD *)DirectoryUserData == 1 ) {

        if ( !Quiet ) {
            if ( ShowWriteableFiles ) {
                printf( "DIR:  " );
            }
            printf( "%ws", Path );
        }

        //
        // If requested, delete this empty directory.
        //

        if ( DeleteEmptyDirectories ) {
            ok = RemoveDirectory( Path );
            if ( !ok ) {
                error = GetLastError( );
                if ( !Quiet ) printf( " - error %d\n", error );
                fprintf( stderr, "Error %d deleting %ws\n", error, Path );
                if ( !ContinueOnError ) {
                    return error;
                }
            } else {
                if ( !Quiet ) printf( " - deleted\n" );
            }
        } else {
            if ( !Quiet ) printf( "\n" );
        }

        //
        // Decrement the parent directory's directory/file count.
        //

        (*(DWORD *)ParentDirectoryUserData)--;
    }

    dprintf(( "  CD: Dir  %ws: parent count %d, dir  count %d\n",
                Path, 
                *(DWORD *)ParentDirectoryUserData, *DirectoryUserData ));

    return 0;

} // CheckDirectory

int
__cdecl
wmain (
    int argc,
    WCHAR *argv[]
    )
{
    BOOL ok;
    DWORD error;
    WCHAR directory[MAX_PATH];
    PVOID scanHandle = NULL;

    //
    // Parse switches.
    //

    argc--;
    argv++;

    while ( (argc != 0) && ((argv[0][0] == '-') || (argv[0][0] == '/')) ) {

        argv[0]++;

        switch ( towlower(argv[0][0]) ) {
        case 'c':
            ContinueOnError = TRUE;
            break;
        case 'd':
            DeleteEmptyDirectories = TRUE;
            break;
        case 'q':
            Quiet = TRUE;
            break;
        case 'w':
            ShowWriteableFiles = TRUE;
            break;
        default:
            fprintf( stderr, "usage: emptydirs [-cdqw]\n" );
            return 1;
        }
        argc--;
        argv++;
    }

    //
    // If a directory was specified, CD to it and get its path.
    //

    if ( argc != 0 ) {
        ok = SetCurrentDirectory( argv[0] );
        if ( !ok ) {
            error = GetLastError( );
            fprintf( stderr, "error: Unable to change to specified directory %ws: %d\n", argv[0], error );
            goto cleanup;
        }
    }
    argc--;
    argv++;

    GetCurrentDirectory( MAX_PATH, directory );

    //
    // Initialize the scan library.
    //

    error = ScanInitialize(
                &scanHandle,
                TRUE,           // recurse
                FALSE,          // don't skip root
                NULL
                );
    if (error != 0) {
        fprintf( stderr, "ScanInitialize(%ws) failed %d\n", directory, error );
        error = 1;
        goto cleanup;
    }

    //
    // Scan the specified directory.
    //

    error = ScanDirectory(
                scanHandle,
                directory,
                NULL,
                NewDirectory,
                CheckDirectory,
                NULL,
                NewFile,
                NULL
                );
    if (error != 0) {
        fprintf( stderr, "ScanDirectory(%ws) failed %d\n", directory, error );
        error = 1;
        goto cleanup;
    }

cleanup:

    //
    // Close down the scan library.
    //

    if ( scanHandle != NULL ) {
        ScanTerminate( scanHandle );
    }

    return error;

} // wmain