/*

  JkDefragCmd  --  Defragment and optimize all harddisks.

  German Version by Markus Hörl  ||  http://jkdefrag.8qm.de or http://www.jkdefrag.de

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  For the full text of the license see the "License gpl.txt" file.



  Jeroen C. Kessels
  Internet Engineer
  http://www.kessels.com/

*/





#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <io.h>                                           /* isatty()*/
#include <time.h>
#include <sys/timeb.h>
#include <tlhelp32.h>                  /* CreateToolhelp32Snapshot() */
#ifdef _DEBUG
#include <dbghelp.h>                   /* SetUnhandledExceptionFilter() */
#endif

#include "JkDefragLib.h"



// Only needed for pre-2008 compiler
#if _MSC_VER < 1500
#include "x64glue.h"
#endif




WCHAR USAGE[] = L"\n\
JkDefrag[Cmd].exe [-a N] [-e \"Maske\"] [-u \"Maske\"] [-s N] [-f N] [-d N] [-l \"Dateiname\"] [Objekte / Laufwerk(e)]\n\
\n\
-a N     Wähle eine Aktion zur Ausführung. Die Nummer N ist ein Wert von 1 bis 11,\n\
         Grundeinstellung ist 3:\n\
         1 = nur analysieren, nicht defragmentieren oder optimieren.\n\
         2 = analysieren und defragmentieren, nicht optimieren.\n\
         3 = analysieren, defragmentieren, und schnell optimieren [empfohlen].\n\
		 4 = seit Version 3.14 entfernt.\n\
         5 = alles zusammenschieben und nicht defragmentieren.\n\
         6 = analysieren, defragmentieren und ans Ende der Festplatte schieben.\n\
         7 = analysieren und Dateien dem Namen nach sortieren (absteigend).\n\
         8 = analysieren und Dateien nach der Größe sortieren (kleinste zuerst).\n\
         9 = analysieren und Dateien nach letztem Zugriff sortieren (neueste zuerst).\n\
         10 = analysieren und Dateien nach letzter Änderung sortieren (älteste zuerst).\n\
         11 = analysieren und Dateien nach Erstellungsdatum sortieren (älteste zuerst).\n\
-e \"Maske\"\n\
         Schließt Dateien und Verzeichnisse aus die mit der Maske übereinstimmen.\n\
         Diese Dateien werden nicht defragmentiert oder verschoben.\n\
         Verwende die Platzhalter '*' und '?' um mehrere Dateien oder Verzeichnisse zu maskieren.\n\
         Wenn die Maske ein Leerzeichen enthält dann muss sie in Anführungsstrichen gesetzt werden.\n\
		 Beispiele: \"*.avi\", \"*\\w3svc*\\*.log\", \"D:\\MySQL\\Data\".\n\
-u \"Maske\"\n\
         Dateien die mit der Maske übereinstimmen werden ans Ende der Daten (nicht ans Ende der Festplatte)\n\
         verschoben. JkDefrag hat eine programminterne Liste für Dateien die größer als 50 MB sind,\n\
         auf die seit einem Monat nicht mehr zugegrifen wurde, für Archive, den Papierkorb,\n\
         Servicepakete und andere Dateien.\n\
         Zum deaktivieren der Liste die spezielle Maske \"DisableDefaults\" eingeben.\n\
         Verwende die Platzhalter '*' und '?' um mehrere Dateien oder Verzeichnisse zu maskieren.\n\
         Wenn die Maske ein Leerzeichen enthält dann muss sie in Anführungszeichen gesetzt werden.\n\
         Beispiele: \"*.avi\", \"*\\w3svc*\\*.log\", \"D:\\MySQL\\Data\\*\".\n\
-s N     Verlangsamt bis N (1...100) Prozent der normalen Geschwindigkeit, Grundeinstellung ist 100.\n\
-f N     Legt die Größe des freien Speicherplatzes fest, die N (1...100) Prozent der Größe der Festplatte ausmachen.\n\
         Die drei freien Speicherplätze sind leere Bereiche für vorübergehende Dateien.\n\
		 Es gibt zwei freie Bereiche zwischen drei Zonen (Verzeichnisse, reguläre Dateien, SpaceHogs).\n\
		 Die Grundeinstellung ist 1%% je freiem Bereich.\n\
-d N     Wähle eine Prüfstufe, um die Meldungen festzulegen die in die Protokolldatei geschrieben werden sollen.\n\
         Die Zahl N ist ein Wert von 0 bis 6, Grundeinstellung ist 1:\n\
         0 = Gravierende Fehler.\n\
         1 = Warnmeldungen (Standardeinstellung).\n\
         2 = Generelle Ablaufmeldungen.\n\
         3 = Detailierte Ablaufmeldungen.\n\
         4 = Detailierte Dateiinformationen.\n\
         5 = Detailierte Meldungen der gefüllten Lücken.\n\
         6 = Detailierte Meldungen der gefundenen Lücken.\n\
-l \"Dateiname\"\n\
         Legt den Dateinamen der Protokolldatei fest. Die Grundeinstellung ist \"JkDefragCmd.log\".\n\
         Um keine Protokolldatei zu erhalten gib zwischen \"\" (zwei Anführungszeichen) keine Zeichen ein.\n\
-h       Zeigt diesen Hilfetext an.\n\
-help    Zeigt diesen Hilfetext an.\n\
--help   Zeigt diesen Hilfetext an.\n\
/?       Zeigt diesen Hilfetext an.\n\
items    Die Objekte die defragmentiert werden sollen, wie eine Datei, ein Verzeichnis,\n\
         eine Festplatte oder ein Laufwerk, einschließlich entfernbarer Medien wie Floppy-Disks,\n\
         USB-Sticks, Speicherkarten und anderen Laufwerken, verhalten sich wie eine Festplatte.\n\
         Die Platzhalter '*' und '?' sind möglich, um mehrere Dateien auszuwählen.\n\
         Wenn ein Name ein Leerzeichen enthält, dann muss es in Anführungszeichen gesetzt werden.\n\
         Beispiele: \"C:\", \"D:\\MySQL\\Data\", \"F:\\*.log\"\n";


WCHAR LogFile[MAX_PATH] = L"";   /* Path to the logfile. */
time_t LastAnalyzeMessage = 0;   /* Time when last analysis message was displayed. */
int IamRunning;
int Running = STOPPED;           /* If not RUNNING then stop defragging. */
int RedrawScreen = 0;            /* 0:no, 1:request, 2: busy. */

/* Debug level.
   0: Fatal errors.
   1: Warning messages.
   2: General progress messages.
   3: Detailed progress messages.
   4: Detailed file information.
   5: Detailed gap-filling messages.
   6: Detailed gap-finding messages.
   */
int Debug;




/* Write a text to the logfile. The parameters are the same as for the "printf"
   functions, a Format string and a series of parameters. */
void LogMessage(WCHAR *Format, ...) {
  va_list VarArgs;
  FILE *Fout;
  int Result;
  time_t Now;
  struct tm NowTm;

  /* If there is no message then return. */
  if (Format == NULL) return;

  /* Write to standard output. */
  va_start(VarArgs,Format);
  vfwprintf_s(stdout,Format,VarArgs);
  va_end(VarArgs);
  fwprintf_s(stdout,L"\n");

  /* If there is no logfile then return. */
  if (*LogFile == '\0') return;

  /* Open the logfile. */
  Result = _wfopen_s(&Fout,LogFile,L"a, ccs=UTF-8");
  if ((Result != 0) || (Fout == NULL)) return;

  /* Write the string to the logfile. */
  time(&Now);
  Result = localtime_s(&NowTm,&Now);
  fwprintf_s(Fout,L"%02lu:%02lu:%02lu ",NowTm.tm_hour,NowTm.tm_min,NowTm.tm_sec);
  va_start(VarArgs,Format);
  vfwprintf_s(Fout,Format,VarArgs);
  va_end(VarArgs);
  fwprintf_s(Fout,L"\n");

  /* Close the logfile. */
  fflush(Fout);
  fclose(Fout);
  }




/* Callback: show a debug message. */
void __cdecl ShowDebug(int Level, struct ItemStruct *Item, WCHAR *Format, ...) {
  va_list VarArgs;
  FILE *Fout;
  int Result;
  time_t Now;
  struct tm NowTm;

  if (Debug < Level) return;

  /* If there is no message then return. */
  if (Format == NULL) return;

  /* Write to standard output. */
  if ((Item != NULL) && (Item->LongPath != NULL)) fwprintf_s(stdout,L"%s\n  ",Item->LongPath);
  va_start(VarArgs,Format);
  vfwprintf_s(stdout,Format,VarArgs);
  va_end(VarArgs);
  fwprintf_s(stdout,L"\n");

  /* If there is no logfile then return. */
  if (*LogFile == '\0') return;

  /* Open the logfile. */
  Result = _wfopen_s(&Fout,LogFile,L"a, ccs=UTF-8");
  if ((Result != 0) || (Fout == NULL)) return;

  /* Write to the logfile. */
  time(&Now);
  Result = localtime_s(&NowTm,&Now);
  fwprintf_s(Fout,L"%02lu:%02lu:%02lu ",NowTm.tm_hour,NowTm.tm_min,NowTm.tm_sec);
  if ((Item != NULL) && (Item->LongPath != NULL)) fwprintf_s(Fout,L"%s\n  ",Item->LongPath);
  va_start(VarArgs,Format);
  vfwprintf_s(Fout,Format,VarArgs);
  va_end(VarArgs);
  fwprintf_s(Fout,L"\n");

  /* Close the logfile. */
  fflush(Fout);
  fclose(Fout);
  }




/* Callback: clear the screen. */
void __cdecl ClearScreen(WCHAR *Format, ...) {
  va_list VarArgs;
  FILE *Fout;
  int Result;
  time_t Now;
  struct tm NowTm;

  /* If there is no message then return. */
  if (Format == NULL) return;

  /* Write to standard output. */
  va_start(VarArgs,Format);
  vfwprintf_s(stdout,Format,VarArgs);
  va_end(VarArgs);
  fwprintf_s(stdout,L"\n");

  /* If there is no logfile then return. */
  if (*LogFile == '\0') return;

  /* Open the logfile. */
  Result = _wfopen_s(&Fout,LogFile,L"a, ccs=UTF-8");
  if ((Result != 0) || (Fout == NULL)) return;

  /* Write to the logfile. */
  time(&Now);
  Result = localtime_s(&NowTm,&Now);
  fwprintf_s(Fout,L"%02lu:%02lu:%02lu ",NowTm.tm_hour,NowTm.tm_min,NowTm.tm_sec);
  va_start(VarArgs,Format);
  vfwprintf_s(Fout,Format,VarArgs);
  va_end(VarArgs);
  fwprintf_s(Fout,L"\n");

  /* Close the logfile. */
  fflush(Fout);
  fclose(Fout);
  }





/* Callback: whenever an item (file, directory) is moved on disk. */
void __cdecl ShowMove(
    struct ItemStruct *Item,
    ULONG64 Clusters,
    ULONG64 FromLcn,
    ULONG64 ToLcn,
    ULONG64 FromVcn) {

  /* If debug mode then write a message to the logfile. */
  if (Debug < 3) return;
  if (FromVcn > 0) {
      if (Clusters == 1) {
          LogMessage(L"%s\n  Verschieben eines Blocks von %I64d nach %I64d, VCN=%I64d.",
            Item->LongPath,FromLcn,ToLcn,FromVcn);
        } else {
          LogMessage(L"%s\n  Verschieben von %I64d Blöcken von %I64d nach %I64d, VCN=%I64d.",
            Item->LongPath,Clusters,FromLcn,ToLcn,FromVcn);
          }
    } else {
      if (Clusters == 1) {
          LogMessage(L"%s\n  Verschieben eines Blocks von %I64d nach %I64d.",
            Item->LongPath,FromLcn,ToLcn);
        } else {
          LogMessage(L"%s\n  Verschieben von %I64d Blöcken von %I64d nach %I64d.",
            Item->LongPath,Clusters,FromLcn,ToLcn);
          }
      }
  }




/* Callback: for every file during analysis.
   This subroutine is called one last time with Item=NULL when analysis has
   finished. */
void __cdecl ShowAnalyze(struct DefragDataStruct *Data, struct ItemStruct *Item) {
  time_t Time;

  /* If debug mode then write a message to the logfile. */
  if (Debug < 3) return;

  Time = time(NULL);
  if (LastAnalyzeMessage == 0) LastAnalyzeMessage = Time;
  if ((Item != NULL) && (LastAnalyzeMessage >= Time)) return;
  LastAnalyzeMessage = Time;
  if (Data != NULL) {
    LogMessage(L"Datei %I64d, Verzeichnis %I64d, Block %I64d",
      Data->CountAllFiles,Data->CountDirectories,Data->CountAllClusters);
    }
  }





/* Callback: paint a cluster on the screen in a color. */
void __cdecl DrawCluster(
    struct DefragDataStruct *Data,
    ULONG64 ClusterStart,
    ULONG64 ClusterEnd,
    int Color) {
  }





/* Callback: just before the defragger starts a new Phase, and when it finishes. */
void __cdecl ShowStatus(struct DefragDataStruct *Data) {
  struct ItemStruct *Item;
  int Fragments;
  ULONG64 TotalFragments;
  ULONG64 TotalBytes;
  ULONG64 TotalClusters;
  struct ItemStruct *LargestItems[25];
  int LastLargest;
  int i;
  int j;

  if (Data != NULL) {
    switch(Data->Phase) {
      case 1: LogMessage(L"Schritt 1: Analysieren"); break;
      case 2: LogMessage(L"Schritt 2: Defragmentieren"); break;
      case 3: LogMessage(L"Schritt 3: Zwangsfüllung"); break;
      case 4: LogMessage(L"Zone %u: Sortieren",Data->Zone + 1); break;
      case 5: LogMessage(L"Zone %u: Optimieren",Data->Zone + 1); break;
      case 6: LogMessage(L"Schritt 3: Nach oben schieben"); break;
      case 7: LogMessage(L"Beendet."); break;
      case 8: LogMessage(L"Schritt 3: Neuverteilung"); break;
      }
    }

  /* Write some statistics to the logfile. */
  if ((Data != NULL) && (Data->Phase == 7)) {
    LogMessage(L"- Gesamte Festplattengröße: %I64d Bytes (%.04f GB), %I64d Blöcke",
      Data->BytesPerCluster * Data->TotalClusters,
      (double)(Data->BytesPerCluster * Data->TotalClusters) / (1024 * 1024 * 1024),
      Data->TotalClusters);
    LogMessage(L"- Bytes je Block: %I64d Bytes",Data->BytesPerCluster);

    LogMessage(L"- Anzahl der Dateien: %I64d",Data->CountAllFiles);
    LogMessage(L"- Anzahl der Verzeichnisse: %I64d",Data->CountDirectories);
    LogMessage(L"- Gesamtgröße der analysierten Einheiten: %I64d Bytes (%.04f GB), %I64d Blöcke",
      Data->CountAllClusters * Data->BytesPerCluster,
      (double)(Data->CountAllClusters * Data->BytesPerCluster) / (1024 * 1024 * 1024),
      Data->CountAllClusters);
    if (Data->CountAllFiles + Data->CountDirectories > 0) {
        LogMessage(L"- Anzahl der fragmentierten Einheiten: %I64d, (%.04f%% aller Einheiten)",
          Data->CountFragmentedItems,
          (double)(Data->CountFragmentedItems * 100) / (Data->CountAllFiles + Data->CountDirectories));
      } else {
        LogMessage(L"- Anzahl der fragmentierten Einheiten: %I64d",Data->CountFragmentedItems);
        }
    if ((Data->CountAllClusters > 0) && (Data->TotalClusters > 0)) {
        LogMessage(L"- Gesamtgröße der fragmentierten Einheiten: %I64d Bytes, %I64d Blöcke (%.04f%% aller Einheiten bzw. %.04f%% der Festplatte)",
          Data->CountFragmentedClusters * Data->BytesPerCluster,
          Data->CountFragmentedClusters,
          (double)(Data->CountFragmentedClusters * 100) / Data->CountAllClusters,
          (double)(Data->CountFragmentedClusters * 100) / Data->TotalClusters);
      } else {
        LogMessage(L"- Gesamtgröße der fragmentierten Einheiten: %I64d Bytes, %I64d Blöcke",
          Data->CountFragmentedClusters * Data->BytesPerCluster,
          Data->CountFragmentedClusters);
        }

    if (Data->TotalClusters > 0) {
        LogMessage(L"- Freier Festplattenplatz: %I64d Bytes, %I64d Blöcke (%.04f%% der Festplatte)",
          Data->CountFreeClusters * Data->BytesPerCluster,
          Data->CountFreeClusters,
          (double)(Data->CountFreeClusters * 100) / Data->TotalClusters);
      } else {
        LogMessage(L"- Freier Festplattenplatz: %I64d Bytes, %I64d Blöcke",
          Data->CountFreeClusters * Data->BytesPerCluster,
          Data->CountFreeClusters);
        }
    LogMessage(L"- Anzahl der Lücken: %I64d",Data->CountGaps);
    if (Data->CountGaps > 0) {
        LogMessage(L"- Anzahl der kleinen Lücken: %I64d (%.04f%% aller Lücken)",
          Data->CountGapsLess16,
          (double)(Data->CountGapsLess16 * 100) / Data->CountGaps);
      } else {
        LogMessage(L"- Anzahl der kleinen Lücken: %I64d",
          Data->CountGapsLess16);
        }
    if (Data->CountFreeClusters > 0) {
        LogMessage(L"- Anzahl der kleinen Lücken: %I64d Bytes, %I64d Blöcke (%.04f%% des freien Festplattenplatzes)",
          Data->CountClustersLess16 * Data->BytesPerCluster,
          Data->CountClustersLess16,
          (double)(Data->CountClustersLess16 * 100) / Data->CountFreeClusters);
      } else {
        LogMessage(L"- Anzahl der kleinen Lücken: %I64d Bytes, %I64d Blöcke",
          Data->CountClustersLess16 * Data->BytesPerCluster,
          Data->CountClustersLess16);
        }
    if (Data->CountGaps > 0) {
        LogMessage(L"- Anzahl der großen Lücken: %I64d (%.04f%% aller Lücken)",
          Data->CountGaps - Data->CountGapsLess16,
          (double)((Data->CountGaps - Data->CountGapsLess16) * 100) / Data->CountGaps);
      } else {
        LogMessage(L"- Anzahl der großen Lücken: %I64d",
          Data->CountGaps - Data->CountGapsLess16);
        }
    if (Data->CountFreeClusters > 0) {
        LogMessage(L"- Größe der großen Lücken: %I64d Bytes, %I64d Blöcke (%.04f%% des freien Festplattenplatzes)",
          (Data->CountFreeClusters - Data->CountClustersLess16) * Data->BytesPerCluster,
          Data->CountFreeClusters - Data->CountClustersLess16,
          (double)((Data->CountFreeClusters - Data->CountClustersLess16) * 100) / Data->CountFreeClusters);
      } else {
        LogMessage(L"- Größe der großen Lücken: %I64d Bytes, %I64d Blöcke",
          (Data->CountFreeClusters - Data->CountClustersLess16) * Data->BytesPerCluster,
          Data->CountFreeClusters - Data->CountClustersLess16);
        }
    if (Data->CountGaps > 0) {
      LogMessage(L"- Durchschnittliche Lückengröße: %.04f Blöcke",
        (double)(Data->CountFreeClusters) / Data->CountGaps);
      }
    if (Data->CountFreeClusters > 0) {
        LogMessage(L"- Größte Lücke: %I64d Bytes, %I64d Blöcke (%.04f%% des freien Festplattenplatzes)",
          Data->BiggestGap * Data->BytesPerCluster,
          Data->BiggestGap,
          (double)(Data->BiggestGap * 100) / Data->CountFreeClusters);
      } else {
        LogMessage(L"- Größte Lücke: %I64d Bytes, %I64d Blöcke",
          Data->BiggestGap * Data->BytesPerCluster,
          Data->BiggestGap);
        }
    if (Data->TotalClusters > 0) {
        LogMessage(L"- Durchschnittliche Ende-Anfang Entfernung: %.0f Blöcke (%.4f%% der Datenträgergröße)",
          Data->AverageDistance,100.0 * Data->AverageDistance / Data->TotalClusters);
      } else {
        LogMessage(L"- Durchschnittliche Ende-Anfang Entfernung: %.0f Blöcke",Data->AverageDistance);
        }

    for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
      if (Item->Unmovable != YES) continue;
      if (Item->Exclude == YES) continue;
      if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue;
      break;
      }
    if (Item != NULL) {
      LogMessage(L"Diese Teile können nicht verschoben werden:");
      LogMessage(L"  Fragmente       Bytes    Blöcke Dateiname mit Pfad");
      TotalFragments = 0;
      TotalBytes = 0;
      TotalClusters = 0;
      for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
        if (Item->Unmovable != YES) continue;
        if (Item->Exclude == YES) continue;
        if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue;
        if ((Item->LongFilename != NULL) &&
            ((_wcsicmp(Item->LongFilename,L"$BadClus") == 0) ||
             (_wcsicmp(Item->LongFilename,L"$BadClus:$Bad:$DATA") == 0))) continue;
        Fragments = FragmentCount(Item);
        if (Item->LongPath == NULL) {
            LogMessage(L"  %9lu %11I64u %9I64u [bei Block %I64u]",Fragments,Item->Bytes,Item->Clusters,
              GetItemLcn(Item));
          } else {
            LogMessage(L"  %9lu %11I64u %9I64u %s",Fragments,Item->Bytes,Item->Clusters,Item->LongPath);
            }
        TotalFragments = TotalFragments + Fragments;
        TotalBytes = TotalBytes + Item->Bytes;
        TotalClusters = TotalClusters + Item->Clusters;
        }
      LogMessage(L"  --------- ----------- --------- ---------");
      LogMessage(L"  %9I64u %11I64u %9I64u Insgesamt",TotalFragments,TotalBytes,TotalClusters);
      }

    for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
      if (Item->Exclude == YES) continue;
      if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue;
      Fragments = FragmentCount(Item);
      if (Fragments <= 1) continue;
      break;
      }
    if (Item != NULL) {
      LogMessage(L"Diese Teile sind noch fragmentiert:");
      LogMessage(L"  Fragmente       Bytes    Blöcke Dateiname mit Pfad");
      TotalFragments = 0;
      TotalBytes = 0;
      TotalClusters = 0;
      for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
        if (Item->Exclude == YES) continue;
        if ((Item->Directory == YES) && (Data->CannotMoveDirs > 20)) continue;
        Fragments = FragmentCount(Item);
        if (Fragments <= 1) continue;
        if (Item->LongPath == NULL) {
            LogMessage(L"  %9lu %11I64u %9I64u [bei Block %I64u]",Fragments,Item->Bytes,Item->Clusters,
              GetItemLcn(Item));
          } else {
            LogMessage(L"  %9lu %11I64u %9I64u %s",Fragments,Item->Bytes,Item->Clusters,Item->LongPath);
            }
        TotalFragments = TotalFragments + Fragments;
        TotalBytes = TotalBytes + Item->Bytes;
        TotalClusters = TotalClusters + Item->Clusters;
        }
      LogMessage(L"  --------- ----------- --------- ---------");
      LogMessage(L"  %9I64u %11I64u %9I64u Insgesamt",TotalFragments,TotalBytes,TotalClusters);
      }

    LastLargest = 0;
    for (Item = TreeSmallest(Data->ItemTree); Item != NULL; Item = TreeNext(Item)) {
      if ((Item->LongFilename != NULL) &&
          ((_wcsicmp(Item->LongFilename,L"$BadClus") == 0) ||
           (_wcsicmp(Item->LongFilename,L"$BadClus:$Bad:$DATA") == 0))) {
        continue;
        }
      for (i = LastLargest - 1; i >= 0; i--) {
        if (Item->Clusters < LargestItems[i]->Clusters) break;
        if ((Item->Clusters == LargestItems[i]->Clusters) &&
            (Item->Bytes < LargestItems[i]->Bytes)) break;
        if ((Item->Clusters == LargestItems[i]->Clusters) &&
            (Item->Bytes == LargestItems[i]->Bytes) &&
            (Item->LongPath != NULL) &&
            (LargestItems[i]->LongPath != NULL) &&
            (_wcsicmp(Item->LongPath,LargestItems[i]->LongPath) > 0)) break;
        }
      if (i < 24) {
        if (LastLargest < 25) LastLargest++;
        for (j = LastLargest - 1; j > i + 1; j--) {
          LargestItems[j] = LargestItems[j-1];
          }
        LargestItems[i + 1] = Item;
        }
      }
    if (LastLargest > 0) {
      LogMessage(L"Die 25 größten Dateien des Laufwerks:");
      LogMessage(L"  Fragmente       Bytes    Blöcke Dateiname mit Pfad");
      for (i = 0; i < LastLargest; i++) {
        if (LargestItems[i]->LongPath == NULL) {
            LogMessage(L"  %9u %11I64u %9I64u [bei Block %I64u]",FragmentCount(LargestItems[i]),
              LargestItems[i]->Bytes,LargestItems[i]->Clusters,GetItemLcn(LargestItems[i]));
          } else {
            LogMessage(L"  %9u %11I64u %9I64u %s",FragmentCount(LargestItems[i]),
              LargestItems[i]->Bytes,LargestItems[i]->Clusters,LargestItems[i]->LongPath);
            }
        }
      }
    }
  }





#ifdef _DEBUG
/* Write a crash report to the log.
   To test the crash handler add something like this:
     char *p1;
     p1 = 0;
     *p1 = 0;
   */
LONG __stdcall CrashReport(EXCEPTION_POINTERS *ExceptionInfo) {
  IMAGEHLP_LINE64 SourceLine;
  DWORD LineDisplacement;
  STACKFRAME64 StackFrame;
  DWORD ImageType;
  BOOL Result;
  int FrameNumber;
  char s1[BUFSIZ];
  WCHAR s2[BUFSIZ];

  /* Exit if we're running inside a debugger. */
//  if (IsDebuggerPresent() == TRUE) return(EXCEPTION_EXECUTE_HANDLER);

  LogMessage(L"Programmabsturz!");
  LogMessage(L"  in Befehlszeile: %s",GetCommandLineW());

  /* Show the type of exception. */
  switch(ExceptionInfo->ExceptionRecord->ExceptionCode) {
    case EXCEPTION_ACCESS_VIOLATION         : strcpy_s(s1,BUFSIZ,"ACCESS_VIOLATION (der Speicher kann nicht gelesen oder beschrieben werden)"); break;
    case EXCEPTION_DATATYPE_MISALIGNMENT    : strcpy_s(s1,BUFSIZ,"DATATYPE_MISALIGNMENT (ein Ausrichtungsfehler eines Datentyps wurde in einer Lade- oder Speicheranweisung festgestellt)"); break;
    case EXCEPTION_BREAKPOINT               : strcpy_s(s1,BUFSIZ,"BREAKPOINT"); break;
    case EXCEPTION_SINGLE_STEP              : strcpy_s(s1,BUFSIZ,"SINGLE_STEP"); break;
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED    : strcpy_s(s1,BUFSIZ,"ARRAY_BOUNDS_EXCEEDED"); break;
    case EXCEPTION_FLT_DENORMAL_OPERAND     : strcpy_s(s1,BUFSIZ,"FLT_DENORMAL_OPERAND"); break;
    case EXCEPTION_FLT_DIVIDE_BY_ZERO       : strcpy_s(s1,BUFSIZ,"FLT_DIVIDE_BY_ZERO"); break;
    case EXCEPTION_FLT_INEXACT_RESULT       : strcpy_s(s1,BUFSIZ,"FLT_INEXACT_RESULT"); break;
    case EXCEPTION_FLT_INVALID_OPERATION    : strcpy_s(s1,BUFSIZ,"FLT_INVALID_OPERATION"); break;
    case EXCEPTION_FLT_OVERFLOW             : strcpy_s(s1,BUFSIZ,"FLT_OVERFLOW"); break;
    case EXCEPTION_FLT_STACK_CHECK          : strcpy_s(s1,BUFSIZ,"FLT_STACK_CHECK"); break;
    case EXCEPTION_FLT_UNDERFLOW            : strcpy_s(s1,BUFSIZ,"FLT_UNDERFLOW"); break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO       : strcpy_s(s1,BUFSIZ,"INT_DIVIDE_BY_ZERO"); break;
    case EXCEPTION_INT_OVERFLOW             : strcpy_s(s1,BUFSIZ,"INT_OVERFLOW"); break;
    case EXCEPTION_PRIV_INSTRUCTION         : strcpy_s(s1,BUFSIZ,"PRIV_INSTRUCTION"); break;
    case EXCEPTION_IN_PAGE_ERROR            : strcpy_s(s1,BUFSIZ,"IN_PAGE_ERROR"); break;
    case EXCEPTION_ILLEGAL_INSTRUCTION      : strcpy_s(s1,BUFSIZ,"ILLEGAL_INSTRUCTION"); break;
    case EXCEPTION_NONCONTINUABLE_EXCEPTION : strcpy_s(s1,BUFSIZ,"NONCONTINUABLE_EXCEPTION"); break;
    case EXCEPTION_STACK_OVERFLOW           : strcpy_s(s1,BUFSIZ,"STACK_OVERFLOW"); break;
    case EXCEPTION_INVALID_DISPOSITION      : strcpy_s(s1,BUFSIZ,"INVALID_DISPOSITION"); break;
    case EXCEPTION_GUARD_PAGE               : strcpy_s(s1,BUFSIZ,"GUARD_PAGE"); break;
    case EXCEPTION_INVALID_HANDLE           : strcpy_s(s1,BUFSIZ,"INVALID_HANDLE"); break;
    case CONTROL_C_EXIT                     : strcpy_s(s1,BUFSIZ,"STATUS_CONTROL_C_EXIT"); break;
    case DBG_TERMINATE_THREAD               : strcpy_s(s1,BUFSIZ,"DBG_TERMINATE_THREAD (Debugger terminated thread)"); break;
    case DBG_TERMINATE_PROCESS              : strcpy_s(s1,BUFSIZ,"DBG_TERMINATE_PROCESS (Debugger terminated process)"); break;
    case DBG_CONTROL_C                      : strcpy_s(s1,BUFSIZ,"DBG_CONTROL_C (Debugger got control C)"); break;
    case DBG_CONTROL_BREAK                  : strcpy_s(s1,BUFSIZ,"DBG_CONTROL_BREAK (Debugger received control break)"); break;
    case DBG_COMMAND_EXCEPTION              : strcpy_s(s1,BUFSIZ,"DBG_COMMAND_EXCEPTION (Debugger command communication exception)"); break;
    default                                 : strcpy_s(s1,BUFSIZ,"(unknown exception)");
    }
  LogMessage(L"  Ausnahmefehler: %S",s1);

  /* Try to show the linenumber of the sourcefile. */
  SymSetOptions(SymGetOptions() || SYMOPT_LOAD_LINES);
  Result = SymInitialize(GetCurrentProcess(),NULL,TRUE);
  if (Result == FALSE) {
    SystemErrorStr(GetLastError(),s2,BUFSIZ);
    LogMessage(L"  Initialisieren von SymInitialize() ist fehlgeschlagen: %s",s2);
    return(EXCEPTION_EXECUTE_HANDLER);
    }
  ZeroMemory(&StackFrame,sizeof(StackFrame));
#ifdef _M_IX86
  ImageType = IMAGE_FILE_MACHINE_I386;
  StackFrame.AddrPC.Offset = ExceptionInfo->ContextRecord->Eip;
  StackFrame.AddrPC.Mode = AddrModeFlat;
  StackFrame.AddrFrame.Offset = ExceptionInfo->ContextRecord->Ebp;
  StackFrame.AddrFrame.Mode = AddrModeFlat;
  StackFrame.AddrStack.Offset = ExceptionInfo->ContextRecord->Esp;
  StackFrame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
  ImageType = IMAGE_FILE_MACHINE_AMD64;
  StackFrame.AddrPC.Offset = ExceptionInfo->ContextRecord->Rip;
  StackFrame.AddrPC.Mode = AddrModeFlat;
  StackFrame.AddrFrame.Offset = ExceptionInfo->ContextRecord->Rsp;
  StackFrame.AddrFrame.Mode = AddrModeFlat;
  StackFrame.AddrStack.Offset = ExceptionInfo->ContextRecord->Rsp;
  StackFrame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
  ImageType = IMAGE_FILE_MACHINE_IA64;
  StackFrame.AddrPC.Offset = ExceptionInfo->ContextRecord->StIIP;
  StackFrame.AddrPC.Mode = AddrModeFlat;
  StackFrame.AddrFrame.Offset = ExceptionInfo->ContextRecord->IntSp;
  StackFrame.AddrFrame.Mode = AddrModeFlat;
  StackFrame.AddrBStore.Offset = ExceptionInfo->ContextRecord->RsBSP;
  StackFrame.AddrBStore.Mode = AddrModeFlat;
  StackFrame.AddrStack.Offset = ExceptionInfo->ContextRecord->IntSp;
  StackFrame.AddrStack.Mode = AddrModeFlat;
#endif
  for (FrameNumber = 1; ; FrameNumber++) {
    Result = StackWalk64(ImageType,GetCurrentProcess(),GetCurrentThread(),&StackFrame,
      ExceptionInfo->ContextRecord,NULL,SymFunctionTableAccess64,SymGetModuleBase64,NULL);
    if (Result == FALSE) break;
    if (StackFrame.AddrPC.Offset == StackFrame.AddrReturn.Offset) break;
    if (StackFrame.AddrPC.Offset != 0) {
      LineDisplacement = 0;
      ZeroMemory(&SourceLine,sizeof(SourceLine));
      SourceLine.SizeOfStruct = sizeof(SourceLine);
      Result = SymGetLineFromAddr64(GetCurrentProcess(),StackFrame.AddrPC.Offset,
        &LineDisplacement,&SourceLine);
      if (Result == TRUE) {
          LogMessage(L"  %i. Bei Zeile %d in '%S'",FrameNumber,SourceLine.LineNumber,SourceLine.FileName);
        } else {
          LogMessage(L"  %i. Bei Zeile (unbekannt) in (unbekannt)",FrameNumber);
/*
          SystemErrorStr(GetLastError(),s2,BUFSIZ);
          LogMessage(L"  Error executing SymGetLineFromAddr64(): %s",s2);
*/
          }
      }
    }

  /* Possible return values:
       EXCEPTION_CONTINUE_SEARCH    = popup a window about the error, user has to click.
       EXCEPTION_CONTINUE_EXECUTION = infinite loop
       EXCEPTION_EXECUTE_HANDLER    = stop program, do not run debugger
     */
  return(EXCEPTION_EXECUTE_HANDLER);
  }
#endif






/* A small wrap-around subroutine to handle Ctrl-C keyboard exit. */
BOOL WINAPI CtrlCHandler(DWORD dwCtrlType) {
  IamRunning = STOPPED;
  StopJkDefrag(&Running,0);
  return(FALSE);
  }




/* If the defragger is not yet running then return NO. If it's already
   running or if there was an error getting the processlist then return
   YES. */
int AlreadyRunning(void) {
  HANDLE Snapshot;
  PROCESSENTRY32 pe32;
  DWORD MyPid;
  char MyName[MAX_PATH];
  WCHAR s1[BUFSIZ];
  WCHAR s2[BUFSIZ];

  /* Get a process-snapshot from the kernel. */
  Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  if (Snapshot == INVALID_HANDLE_VALUE) {
    SystemErrorStr(GetLastError(),s1,BUFSIZ);
    swprintf_s(s2,BUFSIZ,L"Keine Ablaufmomentaufnahme möglich: %s",s1);
    ShowDebug(0,NULL,s2);
    return(YES);
    }
  pe32.dwSize = sizeof(PROCESSENTRY32);

  /* Get my own executable name. */
  MyPid = GetCurrentProcessId();
  *MyName = '\0';
  if (Process32First(Snapshot,&pe32) != FALSE) do {
    if (MyPid == pe32.th32ProcessID) {
      strcpy_s(MyName,MAX_PATH,pe32.szExeFile);
      break;
      }
    } while (Process32Next(Snapshot,&pe32));
  if (*MyName == '\0') {
    /* "Cannot find my own name in the process list: %s" */
    swprintf_s(s1,BUFSIZ,L"Der eigene Name kann nicht in der Ablaufliste gefunden werden: %s",MyName);
    ShowDebug(0,NULL,s1);
    return(YES);
    }

  /* Search for any other process with the same executable name as
     myself. If found then return YES. */
  Process32First(Snapshot,&pe32);
  do {
    if (MyPid == pe32.th32ProcessID) continue;          /* Ignore myself. */
    if ((_stricmp(pe32.szExeFile,MyName) == 0) ||
        (_stricmp(pe32.szExeFile,"jkdefrag.exe") == 0) ||
        (_stricmp(pe32.szExeFile,"jkdefragscreensaver.exe") == 0) ||
        (_stricmp(pe32.szExeFile,"jkdefragcmd.exe") == 0)) {
      CloseHandle(Snapshot);
      swprintf_s(s1,BUFSIZ,L"Läuft bereits: %S",pe32.szExeFile);
      ShowDebug(0,NULL,s1);
      return(YES);
      }
    } while (Process32Next(Snapshot,&pe32));

  /* Return NO, not yet running. */
  CloseHandle(Snapshot);
  return(NO);
  }





int wmain(int argc, WCHAR **argv) {
  int DoAllVolumes;
  int OptimizeMode;
  int Speed;
  double FreeSpace;
  WCHAR **Excludes;
  WCHAR **SpaceHogs;
  time_t Now;
  struct tm NowTm;
  OSVERSIONINFO OsVersion;
  WCHAR *p1;
  int i;

  /* Setup the defaults. */
  Debug = 1;
  OptimizeMode = 2;
  Speed = 100;
  FreeSpace = 1;
  Excludes = NULL;
  SpaceHogs = NULL;
  IamRunning = RUNNING;

  /* Determine default path to logfile. */
  *LogFile = '\0';
  if (GetModuleFileNameW(NULL,LogFile,MAX_PATH) != 0) {
    p1 = stristrW(LogFile,L".exe");
    if (p1 != NULL) {
        *p1 = '\0';
        wcscat_s(LogFile,MAX_PATH,L".log");
      } else {
        *LogFile = '\0';
        }
    }

#ifdef _DEBUG
  /* Setup crash report handler. */
  SetUnhandledExceptionFilter(CrashReport);
#endif

  /* If the program was started in a console then setup a Ctrl-C handler. This
     ensures that the last file will be finished when the user terminates the
     program. */
  if (_isatty(_fileno(stdin)) != 0) SetConsoleCtrlHandler(CtrlCHandler,TRUE);

  /* Scan the commandline arguments for "-l" and setup the logfile. */
  if (argc > 1) {
    for (i = 1; i < argc; i++) {
      if (wcscmp(argv[i],L"-l") == 0) {
        i++;
        if (i >= argc) continue;
        swprintf_s(LogFile,MAX_PATH,L"%s",argv[i]);
        continue;
        }
      if ((wcsncmp(argv[i],L"-l",2) == 0) && (wcslen(argv[i]) >= 3)) {
        swprintf_s(LogFile,MAX_PATH,L"%s",&argv[i][2]);
        continue;
        }
      }
    }
  if (*LogFile != '\0') _wunlink(LogFile);          /* Delete old logfile. */
  LogMessage(VERSIONTEXT);
  time(&Now);
  localtime_s(&NowTm,&Now);
  LogMessage(L"Datum: %04lu/%02lu/%02lu",1900 + NowTm.tm_year,1 + NowTm.tm_mon,NowTm.tm_mday);

  ZeroMemory(&OsVersion,sizeof(OSVERSIONINFO));
  OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if (GetVersionEx(&OsVersion) != 0) {
    LogMessage(L"Windows-Version: v%lu.%lu Build %lu %S",OsVersion.dwMajorVersion,
      OsVersion.dwMinorVersion,OsVersion.dwBuildNumber,OsVersion.szCSDVersion);
    }

  /* Scan the commandline again for all the other arguments. */
  if (argc > 1) {
    for (i = 1; i < argc; i++) {

      if (wcscmp(argv[i],L"-a") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurden keine Zahl nach der \"-a\" Kommandozeileneingabe angegeben.");
          continue;
          }
        OptimizeMode = _wtol(argv[i]);
        if ((OptimizeMode < 1) || (OptimizeMode > 11)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-a\" Kommandozeileneingabe ist ungültig.");
          OptimizeMode = 3;
          }
        OptimizeMode = OptimizeMode - 1;
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-a' wurde akzeptiert. Der Optimierungsmodus ist %u.",OptimizeMode+1);
        continue;
        }
      if (wcsncmp(argv[i],L"-a",2) == 0) {
        OptimizeMode = _wtol(&argv[i][2]);
        if ((OptimizeMode < 1) || (OptimizeMode > 11)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-a\" Kommandozeilen-Option ist ungültig.");
          OptimizeMode = 3;
          }
        OptimizeMode = OptimizeMode - 1;
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-a' wurde akzeptiert. Der Optimierungsmodus ist %u.",OptimizeMode+1);
        continue;
        }

      if (wcscmp(argv[i],L"-s") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurden keine Zahl nach der \"-s\" Kommandozeileneingabe angegeben.");
          continue;
          }
        Speed = _wtol(argv[i]);
        if ((Speed < 1) || (Speed > 100)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-s\" Kommandozeileneingabe ist ungültig.");
          Speed = 100;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-s' wurde akzeptiert. Die Ablaufgeschwindigkeit beträgt %u%%.",Speed);
        continue;
        }
      if ((wcsncmp(argv[i],L"-s",2) == 0) && (wcslen(argv[i]) >= 3)) {
        Speed = _wtol(&argv[i][2]);
        if ((Speed < 1) || (Speed > 100)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-s\" Kommandozeileneingabe ist ungültig.");
          Speed = 100;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-s' wurde akzeptiert. Die Ablaufgeschwindigkeit beträgt %u%%.",Speed);
        continue;
        }

      if (wcscmp(argv[i],L"-f") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurden keine Zahl nach der \"-f\" Kommandozeileneingabe angegeben.");
          continue;
          }
        FreeSpace = _wtof(argv[i]);
        if ((FreeSpace < 0) || (FreeSpace > 100)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-f\" Kommandozeileneingabe ist ungültig.");
          FreeSpace = 1;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-f' wurde akzeptiert. Der freie Speicher beträgt %0.1f%%.",FreeSpace);
        continue;
        }
      if ((wcsncmp(argv[i],L"-f",2) == 0) && (wcslen(argv[i]) >= 3)) {
        FreeSpace = _wtof(&argv[i][2]);
        if ((FreeSpace < 0) || (FreeSpace > 100)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-f\" Kommandozeileneingabe ist ungültig.");
          FreeSpace = 1;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-f' wurde akzeptiert. Der freie Speicher beträgt %0.1f%%.",FreeSpace);
        continue;
        }

      if (wcscmp(argv[i],L"-d") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurden keine Zahl nach der \"-d\" Kommandozeileneingabe angegeben.");
          continue;
          }
        Debug = _wtol(argv[i]);
        if ((Debug < 0) || (Debug > 6)) {
          ShowDebug(0,NULL,L"Fehler: Die Zahl nach der \"-d\" Kommandozeileneingabe ist ungültig.");
          Debug = 1;
          }
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-d' wurde akzeptiert. Die Stufe der Fehlermeldungen beträgt %u.",Debug);
        continue;
        }
      if ((wcsncmp(argv[i],L"-d",2) == 0) && (wcslen(argv[i]) == 3) &&
          (argv[i][2] >= '0') && (argv[i][2] <= '6')) {
        Debug = _wtol(&argv[i][2]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-d' wurde akzeptiert. Die Stufe der Fehlermeldungen beträgt %u.",Debug);
        continue;
        }

      if (wcscmp(argv[i],L"-l") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurde kein Dateiname nach der \"-l\" Kommandozeileneingabe angegeben.");
          continue;
          }
        if (*LogFile != '\0') {
			ShowDebug(0,NULL,L"Kommandozeileneingabe '-l' wurde akzeptiert. Die Protokolldatei heißt: %s",LogFile);
          } else {
            ShowDebug(0,NULL,L"Kommandozeileneingabe '-l' wurde akzeptiert. Die Protokolldatei ist deaktiviert.");
            }
        continue;
        }
      if ((wcsncmp(argv[i],L"-l",2) == 0) && (wcslen(argv[i]) >= 3)) {
        if (*LogFile != '\0') {
			ShowDebug(0,NULL,L"Kommandozeileneingabe '-l' wurde akzeptiert. Die Protokolldatei heißt: %s",LogFile);
          } else {
            ShowDebug(0,NULL,L"Kommandozeileneingabe '-l' wurde akzeptiert. Die Protokolldatei ist deaktiviert.");
            }
        continue;
        }

      if (wcscmp(argv[i],L"-e") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurde kein Pfad für die Maske nach der \"-e\" Kommandozeileneingabe angegeben.");
          continue;
          }
        Excludes = AddArrayString(Excludes,argv[i]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-e' wurde akzeptiert. Es wurde '%s' zur Ausschlussmaske hinzugefügt.",argv[i]);
        continue;
        }
      if ((wcsncmp(argv[i],L"-e",2) == 0) && (wcslen(argv[i]) >= 3)) {
        Excludes = AddArrayString(Excludes,&argv[i][2]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-e' wurde akzeptiert. Es wurde '%s' zur Ausschlussmaske hinzugefügt.",&argv[i][2]);
        continue;
        }

      if (wcscmp(argv[i],L"-u") == 0) {
        i++;
        if (i >= argc) {
          ShowDebug(0,NULL,L"Fehler: Es wurde kein Pfad für die Maske nach der \"-u\" Kommandozeileneingabe angegeben.");
          continue;
          }
        SpaceHogs = AddArrayString(SpaceHogs,argv[i]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-u' wurde akzeptiert. Es wurde '%s' zu den Spacehogs hinzugefügt.",argv[i]);
        continue;
        }
      if ((wcsncmp(argv[i],L"-u",2) == 0) && (wcslen(argv[i]) >= 3)) {
        SpaceHogs = AddArrayString(SpaceHogs,&argv[i][2]);
        ShowDebug(0,NULL,L"Kommandozeileneingabe '-u' wurde akzeptiert. Es wurde '%s' zu den Spacehogs hinzugefügt.",&argv[i][2]);
        continue;
        }

      if ((wcscmp(argv[i],L"-h") == 0) ||
          (wcscmp(argv[i],L"-help") == 0) ||
          (wcscmp(argv[i],L"--help") == 0) ||
          (wcscmp(argv[i],L"/?") == 0)) {
        LogMessage(USAGE);
        return(0);
        }

      if (argv[i][0] == '-') {
        ShowDebug(0,NULL,L"Fehler: Kommandozeileneingabe wird nicht erkannt: %s",argv[i]);
        }
      }
    }

  /* Test if another instance is already running. */
  if (AlreadyRunning() == YES) return(1);

  /* Defragment all the paths that are specified on the commandline one by
     one. */
  DoAllVolumes = YES;
  if (argc > 1) {
    for (i = 1; i < argc; i++) {
      if (IamRunning != RUNNING) break;
      if ((wcscmp(argv[i],L"-a") == 0) ||
          (wcscmp(argv[i],L"-e") == 0) ||
          (wcscmp(argv[i],L"-u") == 0) ||
          (wcscmp(argv[i],L"-s") == 0) ||
          (wcscmp(argv[i],L"-f") == 0) ||
          (wcscmp(argv[i],L"-d") == 0) ||
          (wcscmp(argv[i],L"-l") == 0)) {
        i++;
        continue;
        }
      if (*argv[i] == '-') continue;
      RunJkDefrag(argv[i],OptimizeMode,Speed,FreeSpace,Excludes,SpaceHogs,&Running,
        &RedrawScreen,ShowStatus,ShowMove,ShowAnalyze,ShowDebug,DrawCluster,
        ClearScreen,NULL);
      DoAllVolumes = NO;
      }
    }

  /* If no paths are specified on the commandline then defrag all fixed
     harddisks. */
  if ((DoAllVolumes == YES) && (IamRunning == RUNNING)) {
    RunJkDefrag(NULL,OptimizeMode,Speed,FreeSpace,Excludes,SpaceHogs,&Running,
      &RedrawScreen,ShowStatus,ShowMove,ShowAnalyze,ShowDebug,DrawCluster,
      ClearScreen,NULL);
    }

  IamRunning = STOPPED;

  return(0);
  }
/*
  German Version 3.36 by Markus Hörl  ||  http://jkdefrag.8qm.de or http://www.jkdefrag.de
*/