reactos/rosapps/applications/fraginator/Unfrag.cpp
Daniel Reimer 43ee605045 [FRAGINATOR]
Make the GUI version actually work by disabling wmain per ifdef which will be set only at CUI build.
Fix resources to make the text fit nicely and get rid of the RLEXT/LTEXT mess
Add German translation

svn path=/trunk/; revision=67319
2015-04-20 00:08:02 +00:00

465 lines
14 KiB
C++

/*****************************************************************************
Unfrag
*****************************************************************************/
#include "Unfrag.h"
#include "DriveVolume.h"
#include "Defragment.h"
#include <process.h>
bool QuietMode = false;
bool VerboseMode = false;
// Makes sure we're in Windows 2000
bool CheckWinVer (void)
{
OSVERSIONINFO OSVersion;
ZeroMemory (&OSVersion, sizeof (OSVersion));
OSVersion.dwOSVersionInfoSize = sizeof (OSVersion);
GetVersionEx (&OSVersion);
// Need Windows 2000!
// Check for NT first
// Actually what we do is check that weLL're not on Win31+Win32s and that we're
// not in Windows 9x. It's possible that there could be more Windows "platforms"
// in the future and there's no sense in claiming incompatibility.
if (OSVersion.dwPlatformId == VER_PLATFORM_WIN32s ||
OSVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
{
return (false);
}
// Ok weLL're in Windows NT, now make sure we're in 2000
if (OSVersion.dwMajorVersion < 5)
return (false);
// Kew, we're in at least Windows 2000 ("NT 5.0")
return (true);
}
wchar_t *AddCommas (wchar_t *Result, uint64 Number)
{
wchar_t Temp[128];
int TempLen;
//wchar_t *p = NULL;
int AddCommas = 0;
wchar_t *StrPosResult = NULL;
wchar_t *StrPosOrig = NULL;
// we get the string form of the number, then we count down w/ AddCommas
// while copying the string from Temp1 to Result. when AddCommas % 3 == 1,
// slap in a commas as well, before the #.
swprintf (Temp, L"%I64u", Number);
AddCommas = TempLen = wcslen (Temp);
StrPosOrig = Temp;
StrPosResult = Result;
while (AddCommas)
{
if ((AddCommas % 3) == 0 && AddCommas != TempLen) // avoid stuff like ",345"
{
*StrPosResult = L',';
StrPosResult++;
}
*StrPosResult = *StrPosOrig;
StrPosResult++;
StrPosOrig++;
*StrPosResult = 0;
AddCommas--;
}
return (Result);
}
void PrintBanner (void)
{
wprintf (L"%s v%s\n", APPNAME_CLI, APPVER_STR);
wprintf (L"%s\n", APPCOPYRIGHT);
wprintf (L"\n");
return;
}
void FraggerHelp (void)
{
wprintf (L"Usage: unfrag drive: [...] <-f | -e>\n");
wprintf (L"\n");
wprintf (L"drive: : The drive to defrag. Should be two characters long, ie 'c:' or 'd:'.\n");
wprintf (L" Multiple drives may be given, and all will be simultaneously\n");
wprintf (L" defragmented using the same options.\n");
wprintf (L"-f : Do a fast defragmentation. Files that are not fragmented will not be\n");
wprintf (L" moved. Only one pass is made over the file list. Using this option\n");
wprintf (L" may result in not all files being defragmented, depending on\n");
wprintf (L" available disk space.\n");
wprintf (L"-e : Do an extensive defragmention. Files will be moved in an attempt to\n");
wprintf (L" defragment both files and free space.\n");
if (!CheckWinVer())
{
wprintf (L"\n");
wprintf (L"NOTE: This program requires Windows 2000, which is not presently running on\n");
wprintf (L"this system.\n");
}
return;
}
void __cdecl DefragThread (LPVOID parm)
{
Defragment *Defrag;
Defrag = (Defragment *) parm;
Defrag->Start ();
_endthread ();
return;
}
Defragment *StartDefragThread (wstring Drive, DefragType Method, HANDLE &Handle)
{
Defragment *Defragger;
unsigned long Thread;
Defragger = new Defragment (Drive, Method);
//Thread = /*CreateThread*/ _beginthreadex (NULL, 0, DefragThread, Defragger, 0, &ThreadID);
Thread = _beginthread (DefragThread, 0, Defragger);
Handle = *((HANDLE *)&Thread);
return (Defragger);
}
#ifdef _CUI_
// Main Initialization
extern "C" int wmain (int argc, wchar_t **argv)
{
vector<wstring> Drives;
vector<Defragment *> Defrags;
DefragType DefragMode = DefragInvalid;
PrintBanner ();
// Parse command line arguments
bool ValidCmdLine = false;
for (int c = 0; c < argc; c++)
{
if (wcslen(argv[c]) == 2 && argv[c][1] == L':')
{
Drives.push_back (_wcsupr(argv[c]));
}
else
if ((argv[c][0] == L'-' || argv[c][0] == L'/') && wcslen(argv[c]) == 2)
{
switch (tolower(argv[c][1]))
{
case L'?' :
case L'h' :
FraggerHelp ();
return (0);
case L'f' :
if (DefragMode != DefragInvalid)
{
ValidCmdLine = false;
break;
}
DefragMode = DefragFast;
ValidCmdLine = true;
break;
case L'e' :
if (DefragMode != DefragInvalid)
{
ValidCmdLine = false;
break;
}
DefragMode = DefragExtensive;
ValidCmdLine = true;
break;
}
}
}
if (DefragMode == DefragInvalid)
ValidCmdLine = false;
if (!ValidCmdLine)
{
wprintf (L"Invalid command-line options. Use '%s -?' for help.\n", argv[0]);
return (0);
}
// Check OS requirements
if (!CheckWinVer())
{
wprintf (L"Fatal Error: This program requires Windows 2000.\n");
return (0);
}
for (size_t d = 0; d < Drives.size (); d++)
{
HANDLE TossMe;
Defrags.push_back (StartDefragThread (Drives[d], DefragMode, TossMe));
}
for (size_t d = 0; d < Drives.size () - 1; d++)
wprintf (L"\n ");
bool Continue = true;
HANDLE Screen;
Screen = GetStdHandle (STD_OUTPUT_HANDLE);
while (Continue)
{
Sleep (25);
// Get current screen coords
CONSOLE_SCREEN_BUFFER_INFO ScreenInfo;
GetConsoleScreenBufferInfo (Screen, &ScreenInfo);
// Now set back to the beginning
ScreenInfo.dwCursorPosition.X = 0;
ScreenInfo.dwCursorPosition.Y -= Drives.size();
SetConsoleCursorPosition (Screen, ScreenInfo.dwCursorPosition);
for (size_t d = 0; d < Drives.size (); d++)
{
wprintf (L"\n%6.2f%% %-70s", Defrags[d]->GetStatusPercent(), Defrags[d]->GetStatusString().c_str());
}
// Determine if we should keep going
Continue = false;
for (size_t d = 0; d < Drives.size (); d++)
{
if (!Defrags[d]->IsDoneYet() && !Defrags[d]->HasError())
Continue = true;
}
}
#if 0
// Loop through the drives list
for (int d = 0; d < Drives.size(); d++)
{
DriveVolume *Drive;
Drive = new DriveVolume;
// First thing: build a file list.
wprintf (L"Opening volume %s ...", Drives[d].c_str());
if (!Drive->Open (Drives[d]))
{
wprintf (L"FAILED\n\n");
delete Drive;
continue;
}
wprintf (L"\n");
wprintf (L" Getting drive bitmap ...");
if (!Drive->GetBitmap ())
{
wprintf (L"FAILED\n\n");
delete Drive;
continue;
}
wprintf (L"\n");
wprintf (L" Obtaining drive geometry ...");
if (!Drive->ObtainInfo ())
{
wprintf (L"FAILED\n\n");
delete Drive;
continue;
}
wprintf (L"\n");
wprintf (L" Building file database for drive %s ...", Drives[d].c_str());
if (!Drive->BuildFileList ())
{
wprintf (L"FAILED\n\n");
delete Drive;
continue;
}
wprintf (L"\n");
wprintf (L" %u files\n", Drive->GetDBFileCount ());
// Analyze only?
if (DefragMode == DefragAnalyze)
{
uint64 UsedBytes = 0; // total bytes used, with cluster size considerations
uint64 TotalBytes = 0; // total bytes used
uint64 SlackBytes = 0; // wasted space due to slack
uint32 Fragged = 0; // fragmented files
wprintf (L" Analyzing ...");
if (VerboseMode)
wprintf (L"\n");
for (int i = 0; i < Drive->GetDBFileCount(); i++)
{
uint64 Used;
uint64 Slack;
FileInfo Info;
Info = Drive->GetDBFile (i);
// Compute total used disk space
Used = ((Info.Size + Drive->GetClusterSize() - 1) / Drive->GetClusterSize()) * Drive->GetClusterSize();
Slack = Used - Info.Size;
UsedBytes += Used;
SlackBytes += Slack;
TotalBytes += Info.Size;
if (VerboseMode)
{
wprintf (L" %s%s, ", Drive->GetDBDir (Info.DirIndice).c_str(), Info.Name.c_str());
if (Info.Attributes.AccessDenied)
wprintf (L"access was denied\n");
else
{
if (Info.Attributes.Unmovable == 1)
wprintf (L"unmovable, ");
wprintf (L"%I64u bytes, %I64u bytes on disk, %I64u bytes slack, %u fragments\n",
Info.Size, Used, Slack, Info.Fragments.size());
}
}
if (Info.Fragments.size() > 1)
Fragged++;
}
if (!VerboseMode)
wprintf (L"\n");
// TODO: Make it not look like ass
wprintf (L"\n");
wprintf (L" Overall Analysis\n");
wprintf (L" ----------------\n");
wprintf (L" %u clusters\n", Drive->GetClusterCount ());
wprintf (L" %u bytes per cluster\n", Drive->GetClusterSize());
wprintf (L" %I64u total bytes on drive\n", (uint64)Drive->GetClusterCount() * (uint64)Drive->GetClusterSize());
wprintf (L"\n");
wprintf (L" %u files\n", Drive->GetDBFileCount ());
wprintf (L" %u contiguous files\n", Drive->GetDBFileCount () - Fragged);
wprintf (L" %u fragmented files\n", Fragged);
wprintf (L"\n");
wprintf (L" %I64u bytes\n", TotalBytes);
wprintf (L" %I64u bytes on disk\n", UsedBytes);
wprintf (L" %I64u bytes slack\n", SlackBytes);
}
// Fast defragment!
if (DefragMode == DefragFast || DefragMode == DefragExtensive)
{
uint32 i;
uint64 FirstFreeLCN;
wchar_t PrintName[80];
int Width = 66;
if (DefragMode == DefragFast)
wprintf (L" Performing fast file defragmentation ...\n");
else
if (DefragMode == DefragExtensive)
wprintf (L" Performing extensive file defragmentation\n");
// Find first free LCN for speedier searches ...
Drive->FindFreeRange (0, 1, FirstFreeLCN);
for (i = 0; i < Drive->GetDBFileCount(); i++)
{
FileInfo Info;
bool Result;
uint64 TargetLCN;
wprintf (L"\r");
Info = Drive->GetDBFile (i);
FitName (PrintName, Drive->GetDBDir (Info.DirIndice).c_str(), Info.Name.c_str(), Width);
wprintf (L" %6.2f%% %-66s", (float)i / (float)Drive->GetDBFileCount() * 100.0f, PrintName);
// Can't defrag 0 byte files :)
if (Info.Fragments.size() == 0)
continue;
// If doing fast defrag, skip non-fragmented files
if (Info.Fragments.size() == 1 && DefragMode == DefragFast)
continue;
// Find a place that can fit the file
Result = Drive->FindFreeRange (FirstFreeLCN, Info.Clusters, TargetLCN);
// If we're doing an extensive defrag and the file is already defragmented
// and if its new location would be after its current location, don't
// move it.
if (DefragMode == DefragExtensive && Info.Fragments.size() == 1)
{
if (TargetLCN > Info.Fragments[0].StartLCN)
continue;
}
// Otherwise, defrag0rize it!
if (Result)
{
bool Success = false;
if (Drive->MoveFileDumb (i, TargetLCN))
Success = true;
else
{ // hmm, look for another area to move it to
Result = Drive->FindFreeRange (TargetLCN + 1, Info.Clusters, TargetLCN);
if (Result)
{
if (Drive->MoveFileDumb (i, TargetLCN))
Success = true;
else
{ // Try updating the drive bitmap
if (Drive->GetBitmap ())
{
Result = Drive->FindFreeRange (0, Info.Clusters, TargetLCN);
if (Result)
{
if (Drive->MoveFileDumb (i, TargetLCN))
Success = true;
}
}
}
}
}
if (!Success)
wprintf (L"\n -> failed\n");
Drive->FindFreeRange (0, 1, FirstFreeLCN);
}
}
wprintf (L"\n");
}
wprintf (L"Closing volume %s ...", Drives[d].c_str());
delete Drive;
wprintf (L"\n");
}
#endif
return (0);
}
#endif