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