#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
|
|