Source file: /~heha/hs/filecollect.zip/src/filecollect.cpp

#include <windows.h>
#include <shlwapi.h>
#define elemof(x) (sizeof(x)/sizeof(*(x)))
#define T(x) TEXT(x)

int MaxDepth;
TCHAR ExcludeSpec[MAX_PATH];
TCHAR DateFormat[16]=T("yyMMdd");	// Für Dateinamen, y,yy,yyyy = Jahr, M,MM,MMM,MMMM = Monat, d,dd = Tag
TCHAR ZipDateFormat[16]=T("ddMMyy");	// Für ZIP-Datumsangaben bei Optionen -t, -T; Standard "ddMMyy"
TCHAR TempDir[MAX_PATH];
bool NeedListFiles;	// true wenn $2 in ZIP-Kommandozeile
bool CheckFileOpen;	// true wenn Option "c=1" angegeben (Dateien zur Probe öffnen)
bool IncludeNow;	// true wenn der heutige Tag (Monat, Jahr) mit beachtet werden soll (Option "i=1")
bool UseUTC;		// true wenn das Programm UTC statt Zonenzeit verwenden soll (Option "u=1")
bool DeleteFiles;	// true wenn "x=1" angegeben
bool KeepFiles;		// die Listendateien, nicht die zu archivierenden Dateien!
TCHAR ListDelim[3]=T("\r\n");
TCHAR ListSlash='\\';
UINT ListCP=CP_OEMCP;	// Standards für DOS:PKZIP

/*****************
 * Klasse „Date“ *
 *****************/

struct Date{
 BYTE day;		// 1..31, 0 = kein Vergleich (monatsweise)
 BYTE month;		// 1..12, 0 = kein Vergleich (jahresweise)
 WORD year;		// Jahr mit Jahrhundert
 void Init(SYSTEMTIME&);
 void Init();
 inline Date() {};
 Date(FILETIME&);	// Konstruktor (für Vererbng)
 bool Equal(SYSTEMTIME&) const;
 bool Equal(FILETIME&) const;
 inline bool operator==(FILETIME&ft) const {return Equal(ft);};
 int Format(LPTSTR,int) const;
 static int ZipFormat(SYSTEMTIME&,LPTSTR,int);
 int ZipFormat(LPTSTR,int) const;
 int ZipFormatNext(LPTSTR,int) const;
};

void Date::Init(SYSTEMTIME&st) {
 day=StrChr(DateFormat,'d')?(BYTE)st.wDay:0;
 month=StrChr(DateFormat,'M')?(BYTE)st.wMonth:0;
 year=st.wYear;
}

void Date::Init() {
 SYSTEMTIME st;
 if (UseUTC) GetSystemTime(&st);
 else GetLocalTime(&st);
 Init(st);
}

Date::Date(FILETIME&ft) {
 SYSTEMTIME st;
 FileTimeToSystemTime(&ft,&st);
 Init(st);
}

bool Date::Equal(SYSTEMTIME&st) const {
 return (!day || day==(BYTE)st.wDay)
 && (!month || month==(BYTE)st.wMonth)
 && year==st.wYear;
}

bool Date::Equal(FILETIME&ft) const {
 SYSTEMTIME st;
 FileTimeToSystemTime(&ft,&st);
 return Equal(st);
}

int Date::Format(LPTSTR n, int l) const {
 SYSTEMTIME st={year,month?month:1,0,day?day:1};
 return GetDateFormat(LOCALE_NEUTRAL,0,&st,DateFormat,n,l)-1;
}

int Date::ZipFormat(SYSTEMTIME&st, LPTSTR n, int l) {
 return GetDateFormat(LOCALE_NEUTRAL,0,&st,ZipDateFormat,n,l)-1;
}

int Date::ZipFormat(LPTSTR n, int l) const {
 SYSTEMTIME st={year,month?month:1,0,day?day:1};
 return ZipFormat(st,n,l);
}

int Date::ZipFormatNext(LPTSTR n, int l) const {
 SYSTEMTIME st={year,month?month:1,0,day?day:1};
 if (day) {		// nächsten Tag durch Betriebssystem per Addition von 24h ermitteln
  FILETIME ft;
  SystemTimeToFileTime(&st,&ft);
  *((ULONGLONG*)&ft)+=(ULONGLONG)24*60*60*10000000L;
  FileTimeToSystemTime(&ft,&st);
 }else if (month){	// nächsten Monat zu Fuß erledigen (einfach)
  st.wMonth++;
  if (st.wMonth==13) {
   st.wMonth=1;
   st.wYear++;
  }
 }else st.wYear++;	// nächstes Jahr zu Fuß
 return ZipFormat(st,n,l);
}

Date Now;		// Heutiger Tag (Monat/Jahr)

/*********************
 * Klasse „FileList“ *
 *********************/

struct FileList:Date {
 FileList *next;	// einfach verkette Liste
 TCHAR n[32];		// Dateiname als Datumsangabe (ggf. monats/jahresweise)
 HMMIO h;		// Multimedia-Dateihandle (Pufferung gratis)
 int numfiles;		// Statistik
 ULONGLONG sumsize;	// Statistik
 DWORD exitcode;	// Ergebnis des Pack-Prozesses (sollte 0 sein)
 FileList(FILETIME&);	// Konstruktor
 inline bool Match(FILETIME&ft) const {return Equal(ft);};	// Dieser Topf passt auf Datei-Zeit?
 void Put(LPCTSTR,ULONGLONG);	// Dateiname hinzufügen
 void Flush();		// Datei schließen
 ~FileList();		// Destruktor
 int GetFileName(LPTSTR,int) const;
 void DeleteAll();
 bool ReadLine(char*,int);
};

int FileList::GetFileName(LPTSTR fn,int l) const {
 return wnsprintf(fn,l,T("%s\\%s"),TempDir,n);
}

// Konstruktor: Erstellt Datei für Dateiliste
FileList::FileList(FILETIME&ft):Date(ft) {
 Format(n,elemof(n));
 sumsize=numfiles=0;
 if (NeedListFiles) {
  TCHAR fn[MAX_PATH];
  GetFileName(fn,elemof(fn));
  h=mmioOpen(fn,NULL,MMIO_ALLOCBUF|MMIO_CREATE|MMIO_EXCLUSIVE|MMIO_WRITE);
 }
// TODO: BOM für Unicode-Dateien schreiben
}

// Schließen: Schließt Dateiliste
void FileList::Flush() {
 if (NeedListFiles) {
  mmioClose(h,0);
  h=0;
 }
}

// Textzeile aus Datei lesen, liefert <false> bei Dateiende
bool FileList::ReadLine(char*buf,int len) {
 if (--len<1) return false;
 char c;
// Zeichen lesen bis druckbares Zeichen kommt
 do if (mmioRead(h,&c,1)!=1) return false; while ((unsigned char)c<' ');
// Zeichen lesen bis nicht-druckbares Zeichen (oder Dateiende) kommt
 do *buf++=c; while (mmioRead(h,&c,1)==1 && (unsigned char)c>=' ' && --len);
 *buf=0;
 return true;
}

void FileList::DeleteAll() {
 TCHAR fn[MAX_PATH];
 GetFileName(fn,elemof(fn));
 h=mmioOpen(fn,NULL,MMIO_ALLOCBUF|MMIO_EXCLUSIVE|MMIO_READ);
 char buf[MAX_PATH*2];
 while (ReadLine(buf,sizeof(buf))) {
  TCHAR fname[MAX_PATH];
#ifdef UNICODE
  MultiByteToWideChar(ListCP,0,buf,-1,fname,elemof(fname));
#else
  if (ListCP<1000) OemToCharBuff(buf,fname,elemof(fname));
  else lstrcpyn(fname,buf,elemof(buf));
#endif
  DeleteFile(fname);
 }
 mmioClose(h,0);
}

// Destruktor: Löscht Dateiliste
FileList::~FileList() {
 if (NeedListFiles) {
  if (!exitcode && DeleteFiles) DeleteAll();
  if (!KeepFiles) {
   TCHAR fn[MAX_PATH];
   GetFileName(fn,elemof(fn));
   mmioOpen(fn,NULL,MMIO_DELETE);
  }
 }
}

void FileList::Put(LPCTSTR n,ULONGLONG fsize) {
 numfiles++;
 sumsize+=fsize;	// für Statistik-Ausgabe
 if (NeedListFiles) {
  CHAR buf[MAX_PATH*2];
  int l;
#ifdef UNICODE
  l=WideCharToMultiByte(ListCP,0,n,-1,buf,elemof(buf)-2,NULL,NULL)-1;
  l+=wnsprintfA(buf+l,2,"%S",ListDelim);
#else
  l=wnsprintf(buf,elemof(buf),"%s%s",n,ListDelim);
#endif
  mmioWrite(h,buf,l);
 }
}

FileList *FL;	// Anker aller Datei-Listen

/********************************
 * Objektunabhängige Funktionen *
 ********************************/

// Datei prüfen: zum Archiv (in Dateiliste) hinzufügen?
bool IncludeThisFile(LPCTSTR n, FILETIME &ft) {
 if (PathMatchSpec(n,ExcludeSpec)) return false;
 if (!IncludeNow && Now==ft) return false;
 if (NeedListFiles && CheckFileOpen) {
  HANDLE h=CreateFile(n,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,0);
  if (h==INVALID_HANDLE_VALUE) return false;	// lässt sich nicht öffnen
  CloseHandle(h);
 }
 return true;
}

// Eine gefundene Datei in passende Dateiliste schreiben
void PutFileToList(LPCTSTR n, FILETIME &ft,ULONGLONG fsize) {
 FileList *p;
 for (p=FL; p; p=p->next) {
  if (p->Match(ft)) break;
 }
 if (!p) {
  p=new FileList(ft);
  p->next=FL;
  FL=p;
 }
 p->Put(n,fsize);
}

// Rekursive Funktion, Erstaufruf mit Leerstring
void SearchDirectory(LPCTSTR root, int depth) {
 TCHAR n[MAX_PATH+2];
 wnsprintf(n,elemof(n),T("%s*"),root);
 WIN32_FIND_DATA fd;
 HANDLE h=FindFirstFile(n,&fd);
 if (h!=INVALID_HANDLE_VALUE) {
  do{
   wnsprintf(n,elemof(n),T("%s%s"),root,fd.cFileName);
   if (fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) {
    if (depth!=MaxDepth
    && lstrcmp(fd.cFileName,T("."))
    && lstrcmp(fd.cFileName,T(".."))) {
     int l=lstrlen(n);
     n[l]=ListSlash;
     n[l+1]=0;
//     PathAddBackslash(n);
     SearchDirectory(n,depth+1);
    }
   }else{
    FILETIME ft;
    if (UseUTC) ft=fd.ftLastWriteTime;
    else FileTimeToLocalFileTime(&fd.ftLastWriteTime,&ft);
    if (IncludeThisFile(n,ft)) {
     LARGE_INTEGER ll={fd.nFileSizeLow,fd.nFileSizeHigh};
     PutFileToList(n,ft,ll.QuadPart);
    }
   }
  }while (FindNextFile(h,&fd));
  FindClose(h);
 }
}

#ifdef _M_X86
// hier: Einschränkung, nur für DWORD-Divisor
EXTERN_C ULONGLONG _declspec(naked) _cdecl _aulldiv(ULONGLONG z, ULONGLONG n) {
 _asm{
	mov	eax,[esp+8]	;High-Teil Zähler
	mov	ecx,[esp+12]	;Nenner
	xor	edx,edx
	div	ecx		;EAX=Ergebnis, EDX=Rest
	push	eax
	 mov	eax,[esp+8]	;Low-Teil Zähler
	 div	ecx		;EAX=Ergebnis, EDX=Rest
	pop	edx		;Rest durch High-Teil ersetzen
	ret	16
 }
}
#endif

// Wartet auf morgen, bis ca. 0:03
void WarteAufMorgen() {
 SYSTEMTIME st;
 if (UseUTC) GetSystemTime(&st); else GetLocalTime(&st);
 FILETIME ft,ft2;
 SystemTimeToFileTime(&st,&ft);
 *((ULONGLONG*)&ft2)=*((ULONGLONG*)&ft)+(ULONGLONG)24*60*60*10000000L;
 FileTimeToSystemTime(&ft2,&st);
 st.wHour=0;
 st.wMinute=3;
 st.wSecond=0;
 st.wMilliseconds=0;
 SystemTimeToFileTime(&st,&ft2);
 Sleep(DWORD(((*(ULONGLONG*)&ft2)-(*(ULONGLONG*)&ft))/10000UL));	// Zeitdifferenz in Millisekunden
}

HANDLE stdout;

// Auf Konsole schreiben
int vprintf(LPTSTR s, va_list va) {
 TCHAR buf[1024];
 DWORD l=wvnsprintf(buf,elemof(buf),s,va);
#ifndef UNICODE
 AnsiToOemBuff(buf,buf,l);	// Gehen wir mal davon aus, dass die ANSI-Version nicht unter WinNT zum Einsatz kommt
#endif				// und dass Win9x/Me „chcp“ nicht wirklich unterstützt.
 WriteConsole(stdout,buf,l,&l,NULL);	// Die Unicode-Version läuft auch korrekt, wenn „chcp 65001“ gesetzt
 return l;				// und ein Unicode-Font („Lucidia Console“) ausgewählt wurde.
}

int _cdecl printf(LPTSTR s, ...) {
 return vprintf(s,(va_list)(&s+1));
}

/*****************
 * Hauptprogramm *
 *****************/

EXTERN_C void _declspec(noreturn) _cdecl mainCRTStartup() {
 stdout=GetStdHandle(STD_OUTPUT_HANDLE);
 LPTSTR CmdLine=GetCommandLine();
 lstrcpy(ExcludeSpec,T("*.zip;*.$$$"));
/* Kommandozeilenparameter auswerten */
// AWK-Syntax der Optionen: Variablenname=Wert
// Danach folgt Kommandozeile des Packers
// S.a. CommandLineToArgvW()
 LPTSTR a,b;
 a=PathGetArgs(CmdLine);
 if (!a || !*a) { printf(
     T("FileCollect h#s 100322: Aufruf Packer tages/wochen/monatsweise\n")
     T("Optionen (im AWK-Stil!) mit Vorgabe:\n")
     T("  c=%d		Öffnet jede Datei probeweise vor Einfügung in Liste bei »1«\n")
     T("  d=%s	Datumsformatfestlegung, google nach »GetDateFormat«\n")
     T("		Weglassen von »dd« führt zu monatlichen Archiven\n")
     T("		Weglassen von »MM« und »dd« führt zu jährlichen Archiven\n")
     T("  e=%s	Diese Dateien von Listendatei(en) ausschließen\n")
     T("  j=%d		Heutigen Tag (Monat/Jahr) einschließen bei »1«\n")
     T("  k=%d		Listendateien behalten wenn »1«\n")
     T("  n=%d		Rekursionstiefe der Listendateierstellung, »0« = unbegrenzt\n")
     T("  p=%c		Pfad-Trenner in Listendatei\n")
     T("  u=%d		Zonenzeit verwenden bei »0«, UTC bei »1«\n")
     T("  w=%s	Zeilentrenner in Listendatei\n")
     T("  x=%d		Dateien der Dateiliste bei Erfolg löschen wenn »1«\n")
     T("  y=%d		Codeseite der Listendatei, 850=DOS, 1252=Win, 65001=UTF-8\n")
     T("  z=%s	Datumsformatfestlegung für (ZIP-)Packer\n")
     T("		(PKZIP kann selbst nach Datum ohne Dateiliste packen)\n"),
         CheckFileOpen,DateFormat,ExcludeSpec,IncludeNow,KeepFiles,MaxDepth,ListSlash,UseUTC,
	 ListDelim[0]=='\r'?ListDelim[1]?T("CRLF"):T("CR\t"):T("LF\t"),
	 DeleteFiles,ListCP,ZipDateFormat); printf(
     T("Danach folgt die Kommandozeile für den Packer mit folgenden Ersetzungszeichen:\n")
     T("	$1	Listen-Dateiname (mit Pfad, im Temp-Verzeichnis)\n")
     T("	$2	Datums-String entsprechend Festlegung bei »d=«\n")
     T("	$3	Datums-String entsprechend Festlegung bei »z=«\n")
     T("	$4	Datums-String des nächsten Tages (Monats/Jahres) von »z=«\n")
     T("	$5	Ausgeschlossene Dateien entsprechend »e=«\n")
     T("	$$	Literales »$«\n")); printf(
     T("Die Pack-Prozesse werden in separaten Fenstern mit Idle-Priorität gestartet.\n")
     T("Ausgangspunkt ist das aktuelle Verzeichnis.\n")
     T("Bei erfolgreichem Abschluss aller Packprozesse wartet das Programm\n")
     T("auf den Beginn des nächsten Tages und startet dann 0:03 Uhr erneut.\n")
     T("Beispiel:	»pkzip -u -o -m -r -p -t$3 -T$4 $2.zip« (ohne Listendateien)\n")
     T("»c=1 e=*.bz2 tar -cvjf $2.tar.bz2 --remove-files -T $1« (ohne Update+Verify)\n")
     T("»c=1 p=/ w=LF x=1 y=65001 7za u $2.zip -i@$1« (7-zip kann nicht selbst löschen)\n")
  );
  ExitProcess(1);
 }
 while (a && *a) {
  b=PathGetArgs(a);
  if (a[1]=='=') {
   if (b && *b) b[-1]=0;	// terminieren
   StrTrim(a+2,T("\" \t"));	// eventuelle Leerzeichen weg!
   switch (*a) {
    case 'c': CheckFileOpen=StrToInt(a+2)!=0; break;
    case 'd': lstrcpyn(DateFormat,a+2,elemof(DateFormat)); break;	// Google „GetDateFormat“
    case 'e': lstrcpyn(ExcludeSpec,a+2,elemof(ExcludeSpec)); break;
    case 'j': IncludeNow=StrToInt(a+2)!=0; break;
    case 'k': KeepFiles=StrToInt(a+2)!=0; break;
    case 'n': MaxDepth=StrToInt(a+2)!=0; break;
    case 'p': ListSlash=a[2]; break;
    case 'u': UseUTC=StrToInt(a+2)!=0; break;
    case 'w': {
     ListDelim[0]=a[2]=='C'?'\r':'\n';
     ListDelim[1]=a[4]=='C'?'\r':a[4]=='L'?'\n':0;
    }break;
    case 'x': DeleteFiles=StrToInt(a+2)!=0; break;
    case 'y': ListCP=StrToInt(a+2); break;
    case 'z': lstrcpyn(ZipDateFormat,a+2,elemof(ZipDateFormat)); break;
   }
  }else break;
  a=b;
 }
 if (StrStr(a,T("$1"))) NeedListFiles=true;	// unsauber!
 if (!StrCmpNI(a,T("tar"),3)) {
  lstrcpy(ListDelim,T("\n"));	// QUIRKS: UNIX-Listentrenner
  ListSlash='/';		// UNIX-Pfadtrenner
 }
 printf(T("Kommandozeile geparst, wirksame Schalter:\n"));
 printf(T("	Listendateien generieren: %d\n"),NeedListFiles);
 printf(T("	Datumsformatfestlegung: »%s«\n"),DateFormat);
 if (NeedListFiles) {
  printf(T("	Dateien probeweise öffnen: %d\n"),CheckFileOpen);
  printf(T("	Dateien von Liste ausschließen: »%s«\n"),ExcludeSpec);
  printf(T("	Listendatei behalten: %d\n"),KeepFiles);
  printf(T("	Pfad-Separator: %c\n"),ListSlash);
  printf(T("	Zeilen-Separator: %s\n"),ListDelim[0]=='\r'?ListDelim[1]?T("CRLF"):T("CR\t"):T("LF\t"));
  printf(T("	Codeseite der Listendatei (stets ohne BOM): %d\n"),ListCP);
 }
 printf(T("	Heutigen Tag (Monat/Jahr) einschließen: %d\n"),IncludeNow);
 if (NeedListFiles) {
  printf(T("	Rekursionstiefe (0 = unbegrenzt): %d\n"),MaxDepth);
 }
 printf(T("	UTC statt Zonenzeit verwenden: %d\n"),UseUTC);
 if (StrStr(a,T("$3")) || StrStr(a,T("$4"))) {
  printf(T("	Datumsformatfestlegung für ZIP-Packer: »%s«\n"),ZipDateFormat);
 }
 TCHAR curdir[MAX_PATH];
 GetCurrentDirectory(elemof(curdir),curdir);
 printf(T("	Aktuelles Verzeichnis: »%s«\n"),curdir);
 printf(T("	Kommandozeile für Packer: »%s«\n"),a);
repeat:
/* Hier geht's los! */
 DWORD start=GetTickCount();
 printf(T("\nDateien-Suche beginnt (zumindest für benötigte Datumsangaben) ...\n"));
 Now.Init();
 if (NeedListFiles) {
  TCHAR TempPath[MAX_PATH];
  GetTempPath(elemof(TempPath),TempPath);
  GetTempFileName(TempPath,T("h#s"),0,TempDir);	// erzeugt diese Datei!
  DeleteFile(TempDir);
  CreateDirectory(TempDir,NULL);		// Stattdessen Verzeichnis erzeugen
 }
// Dateiliste(n) erstellen
 SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_BELOW_NORMAL);
 SearchDirectory(T(""),1);
 SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL);
 printf(T("... fertig, Statistik für Packjobs:\n"));
// Anzahl Packjobs zählen und Handle-Array erstellen
 int nJobs=0;
 FileList *p;
 for (p=FL; p; p=p->next) {
  p->Flush();		// Dateien schließen
  TCHAR s[32];
  StrFormatByteSize64(p->sumsize,s,elemof(s));
  printf(T("	Für Datum %s: %d Dateien, Gesamtgröße %s\n"),p->n,p->numfiles,s);
  nJobs++;
 }
 printf(T("%d Pack-Jobs starten jetzt in getrennten Konsolenfenstern ...\n"),nJobs);
 HANDLE *Jobs = new HANDLE[nJobs];
// Für jede der Dateilisten Packer aufrufen
 int i=0;
 for (p=FL; p; p=p->next) {
  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  GetStartupInfo(&si);
  TCHAR cmd[1024];
  LPCTSTR s=a;
  LPTSTR d=cmd;
  TCHAR c;
  for (;c=*s++;) {
   if (c=='$') {
    c=*s++;
    switch (c) {
     case '1': d+=p->GetFileName(d,int(cmd+elemof(cmd)-d)); break;
     case '2': d+=p->Format(d,int(cmd+elemof(cmd)-d)); break;
     case '3': d+=p->ZipFormat(d,int(cmd+elemof(cmd)-d)); break;
     case '4': d+=p->ZipFormatNext(d,int(cmd+elemof(cmd)-d)); break;
     case '5': d+=wnsprintf(d,int(cmd+elemof(cmd)-d),T("%s"),ExcludeSpec); break;
     case '$': if (d<cmd+elemof(cmd)-1) *d++=c; break;
     default:  if (d<cmd+elemof(cmd)-2) *d++='$'; *d++=c;	// Fehlertoleranz: unverändert durchlassen
    }
   }else if (d<cmd+elemof(cmd)-1) *d++=c;
  }
  *d=0;
  printf(T("Starte: »%s«\n"),cmd);
  if (!CreateProcess(NULL,cmd,NULL,NULL,FALSE,IDLE_PRIORITY_CLASS|CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) {
   LPTSTR p;
   DWORD code=GetLastError();
   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
     NULL,code,
     MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&p,0,NULL);
   printf(T("ABBRUCH: Start versagt, Fehlerkode %d, Text:\n%s"),code,p);
   LocalFree(p);
   ExitProcess(2);
  }
  Jobs[i++]=pi.hProcess;
 }
// Warten
 WaitForMultipleObjects(nJobs,Jobs,TRUE,INFINITE);
 printf(T("... fertig, Aufräumarbeiten\n"));
 bool AllesOK=true;
 for (i=0, p=FL; i<nJobs; i++, p=p->next) {
  GetExitCodeProcess(Jobs[i],&p->exitcode);
  CloseHandle(Jobs[i]);
  printf(T("	Für Datum %s: Ergebniskode (0 = fehlerfrei): %d\n"),p->n,p->exitcode);
  if (p->exitcode) AllesOK=false;
 }
 delete[] Jobs;
// Aufräumen
 while (FL) {
  p=FL->next;
  delete FL;
  FL=p;
 }
 if (NeedListFiles && !KeepFiles) RemoveDirectory(TempDir);
 TCHAR diff[32];
 StrFromTimeInterval(diff,elemof(diff),GetTickCount()-start,6);
 StrTrim(diff,T(" "));
 if (AllesOK) {
  printf(T("Alle Packprozesse OK, hat %s benötigt, schlafe bis morgen 0:03 ...\n"),diff);
  WarteAufMorgen();
  TCHAR s[32];
  GetTimeFormat(LOCALE_USER_DEFAULT,TIME_FORCE24HOURFORMAT,NULL,NULL,s,elemof(s));
  printf(T("... wieder aufgeweckt, Uhrzeit (Zonenzeit): %s\n"),s);
  goto repeat;
 }else{
  printf(T("Packprozess mit Fehler nach %s, Programmende! Admin, hilf!\n"),diff);
 }
 ExitProcess(2);
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded