337 lines
7.4 KiB
C
337 lines
7.4 KiB
C
#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
|
|
|