- Start rosapps rearrange and cleanup process.
[reactos.git] / rosapps / applications / fraginator / Unfrag.cpp
diff --git a/rosapps/applications/fraginator/Unfrag.cpp b/rosapps/applications/fraginator/Unfrag.cpp
new file mode 100644 (file)
index 0000000..7780256
--- /dev/null
@@ -0,0 +1,463 @@
+/*****************************************************************************\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