unit dateiBeziehungen; {$mode objfpc}{$H+} interface uses classes, sysUtils, tools, regExpr; type tZeilenTyp = (ztSuche,ztZiel,ztQuelle,ztBefehl); pTAbhaengigkeit = ^tAbhaengigkeit; tAbhaengigkeit = class; tAbhaengigkeiten = class(tFPList) private function rItem(idx: longint): tAbhaengigkeit; inline; procedure wItem(idx: longint; neu: tAbhaengigkeit); inline; public property items[idx: longint]: tAbhaengigkeit read rItem write wItem; default; procedure kopiereVon(original: tAbhaengigkeiten); procedure mrProper; function add(neu: tAbhaengigkeit): longint; function last: tAbhaengigkeit; end; tAbhaengigkeit = class(tObject) quellenRE: tRegExpr; zieleFkt, befehle: tStringlist; quellen, ziele: tDateienMitDaten; erben: tAbhaengigkeiten; prioritaet: longint; constructor create; overload; constructor create(original: tAbhaengigkeit); overload; destructor destroy; override; end; tMach = class private _machDatei,_pruefsummenDatei: string; _oriAbh,_mglAbh,_ztAbh: tAbhaengigkeiten; _dats: tDateienMitDaten; procedure wMachDatei(md: string); procedure wPruefsummenDatei(pd: string); function liesMachDatei: boolean; function liesPruefsummenfile: boolean; public property machDatei: string read _machDatei write wMachDatei; property pruefsummenDatei: string read _pruefsummenDatei write wPruefsummenDatei; constructor create; destructor destroy; override; procedure erzeugeRegeln; procedure findeWasZuTunIst; procedure tueWasZuTunIst(allesNeu, unsicher: boolean; ausgabeDatei: string); procedure setzeGenerischePruefsummenDatei; function anzOriAbh: longint; inline; function anzMglAbh: longint; inline; function anzZtAbh: longint; inline; function anzDats: longint; inline; end; function quellersetzung(var worin: string; quelle, inputfile: string): boolean; procedure sammleDateien(wo: string; rekursiv: boolean; ignoriere: array of tRegExpr; var dats: tDateienMitDaten); procedure zieleHinzufuegen(var ziele: tDateienMitDaten; ziel,quelle,inputfile: string; var dats: tDateienMitDaten); procedure findeMehrZiele(var ziele: tDateienMitDaten; zieleFkt: tStringList; quelle,inputfile: string; var dats: tDateienMitDaten); implementation uses lowlevelunit, mystringlistunit, math; // tAbhaengigkeiten ************************************************************ function tAbhaengigkeiten.rItem(idx: longint): tAbhaengigkeit; begin result:=tAbhaengigkeit(get(idx)); end; procedure tAbhaengigkeiten.wItem(idx: longint; neu: tAbhaengigkeit); begin put(idx,neu); end; procedure tAbhaengigkeiten.kopiereVon(original: tAbhaengigkeiten); var i: longint; begin clear; for i:=0 to original.count-1 do add(original[i]); end; procedure tAbhaengigkeiten.mrProper; var i: longint; begin for i:=0 to count-1 do items[i].free; clear; end; function tAbhaengigkeiten.add(neu: tAbhaengigkeit): longint; begin result:=inherited add(neu); end; function tAbhaengigkeiten.last: tAbhaengigkeit; begin result:=tAbhaengigkeit(inherited last); end; // tAbhaengigkeit ************************************************************** constructor tAbhaengigkeit.create; begin inherited create; quellenRE:=tRegExpr.create; quellenRE.expression:=''; zieleFkt:=tStringlist.create; setlength(quellen,0); setlength(ziele,0); befehle:=tStringlist.create; erben:=tAbhaengigkeiten.create; prioritaet:=42; end; constructor tAbhaengigkeit.create(original: tAbhaengigkeit); var i: longint; begin inherited create; quellenRE:=tRegExpr.create; quellenRE.expression:=original.quellenRE.expression; zieleFkt:=tStringlist.create; zieleFkt.text:=original.zieleFkt.text; setlength(quellen,length(original.quellen)); for i:=0 to length(original.quellen)-1 do quellen[i]:=original.quellen[i]; setlength(ziele,length(original.ziele)); for i:=0 to length(original.ziele)-1 do ziele[i]:=original.ziele[i]; befehle:=tStringlist.create; befehle.text:=original.befehle.text; erben:=tAbhaengigkeiten.create; erben.kopiereVon(original.erben); prioritaet:=original.prioritaet; end; destructor tAbhaengigkeit.destroy; begin quellenRE.free; setLength(quellen,0); setLength(ziele,0); zieleFkt.free; befehle.free; erben.free; inherited destroy; end; // tMach *********************************************************************** constructor tMach.create; begin inherited create; _oriAbh:=tAbhaengigkeiten.create; _mglAbh:=tAbhaengigkeiten.create; _ztAbh:=tAbhaengigkeiten.create; setlength(_dats,0); end; destructor tMach.destroy; var i: longint; begin _oriAbh.mrProper; _oriAbh.free; _mglAbh.mrProper; _mglAbh.free; _ztAbh.mrProper; _ztAbh.free; for i:=0 to length(_dats)-1 do _dats[i].free; setlength(_dats,0); inherited destroy; end; procedure tMach.wMachDatei(md: string); begin _machDatei:=md; if not fileexists(_machDatei) then fehler('Datei '''+_machDatei+''' existiert nicht!'); if not liesMachDatei then fehler('Datei '''+_machDatei+''' ist fehlerhaft!'); end; procedure tMach.wPruefsummenDatei(pd: string); begin _pruefsummenDatei:=pd; if not liesPruefsummenfile then fehler('Datei '''+_pruefsummenDatei+''' ist fehlerhaft!'); end; function tMach.liesMachDatei: boolean; var f: tMyStringList; s: string; na: tAbhaengigkeit; ignoriere: array of tRegExpr; rek: boolean; num,posi: longint; wasWar,wasIst: tZeilenTyp; procedure aufraeumen; var ii: longint; begin f.free; na.free; for ii:=0 to length(ignoriere)-1 do ignoriere[ii].free; setlength(ignoriere,0); end; begin result:=false; na:=tAbhaengigkeit.create; na.prioritaet:=0; setlength(ignoriere,0); num:=0; wasWar:=ztSuche; f:=tMyStringList.create; f.loadFromFile(machDatei); f.add('%%DATEIENDE%%'); if not f.unfoldMacros then begin f.free; exit; end; s:=''; while f.readln(s) do begin if rightStr(s,1)=';' then // ein Befehl(steil) wasIst:=ztBefehl else if rightStr(s,1)=':' then // ein Ziel wasIst:=ztZiel else if wasWar=ztZiel then // eine Quelle wasIst:=ztQuelle else // eine zu überwachende Datei wasIst:=ztSuche; if wasIst in [ztZiel,ztBefehl] then delete(s,length(s),1); if (wasWar=ztBefehl) and (wasIst<>ztBefehl) then begin _oriAbh.add(na); inc(num); na:=tAbhaengigkeit.create; na.prioritaet:=num; end; if s='%%DATEIENDE%%' then break; case wasIst of ztSuche: begin if not quellersetzung(s,'',machDatei) then begin writeln('Fehler: Quellersetzung in '''+s+''' fehlgeschlagen!'); aufraeumen; exit; end; if pos('!',s)=1 then begin delete(s,1,1); s:=trim(s); setlength(ignoriere,length(ignoriere)+1); ignoriere[length(ignoriere)-1]:=tRegExpr.create; ignoriere[length(ignoriere)-1].expression:=s; end else begin rek:=startetMit('-r',s); while s<>'' do sammleDateien(erstesArgument(s),rek,ignoriere,_dats); end; end; ztBefehl: begin while unescapedpos(';',s)>0 do begin posi:=unescapedpos(';',s); s:=trim(leftStr(s,posi-1))+' && '+trim(rightStr(s,length(s)-posi)); end; while pos(';;',s)>0 do delete(s,pos(';;',s),1); na.befehle.add(s); end; ztZiel: while s<>'' do na.zieleFkt.add(erstesArgument(s)); ztQuelle: begin if not quellersetzung(s,'',machDatei) then begin writeln('Fehler: Quellersetzung in '''+s+''' fehlgeschlagen!'); aufraeumen; exit; end; na.quellenRE.expression:=s; end; end{of case}; wasWar:=wasIst; end; if s<>'%%DATEIENDE%%' then writeln('Interner Fehler! Die letzte Regel wird vsl. nicht beachtet!'); aufraeumen; result:=true; end; function tMach.liesPruefsummenfile: boolean; var f: textfile; i,j: longint; gutschlecht: tStringlistBArray; gefunden,gut: boolean; begin result:=false; if pruefsummenDatei='' then begin writeln('Leerer Name als Summendatei angegeben!'); exit; end; if not fileexists(pruefsummenDatei) then begin assignfile(f,pruefsummenDatei); rewrite(f); closefile(f); end; if not fileexists(pruefsummenDatei) then begin writeln('Ich bin nicht in der Lage, die bisher nicht existierende Datei '''+pruefsummenDatei+''' anzulegen!'); exit; end; gutschlecht:=testeSummen(pruefsummenDatei); for gut:=false to true do for i:=0 to gutschlecht[gut].count-1 do begin gefunden:=false; for j:=0 to length(_dats)-1 do if _dats[j].name=gutschlecht[gut][i] then begin if gut then _dats[j].aktuell:=aAktuell else _dats[j].aktuell:=aVeraltet; gefunden:=true; break; end; if not gefunden then begin if not gut then begin // dateien mit ungültiger Prüfsumme können auch nicht vorhanden sein, setlength(_dats,length(_dats)+1); // dann werden sie einfach eingefügt _dats[length(_dats)-1]:=tDateiMitDatum.create; _dats[length(_dats)-1].name:=gutschlecht[gut][i]; _dats[length(_dats)-1].aktuell:=aVeraltet; continue; end; writeln('In der Summendatei gibt es eine Datei mit gültiger Prüfsumme, die ich nicht finden kann: '''+gutschlecht[gut][i]+'''!'); for gefunden:=false to true do gutschlecht[gefunden].free; exit; end; end; for gefunden:=false to true do gutschlecht[gefunden].free; result:=true; end; procedure tMach.erzeugeRegeln; var i,j,k,l: longint; neues,schonDa: boolean; s: string; tmpZiele: tDateienMitDaten; begin repeat neues:=false; for i:=0 to _oriAbh.count-1 do for j:=0 to length(_dats)-1 do if _oriAbh[i].quellenRE.exec(_dats[j].name) then begin schonDa:=false; for k:=0 to _oriAbh[i].erben.count-1 do for l:=0 to length(_oriAbh[i].erben[k].quellen)-1 do schonDa:=schonDa or (_oriAbh[i].erben[k].quellen[l]=_dats[j]); // Falls diese Quelle schon in eine Regel einfließt, brauchen // wir nichts mehr zu machen, da es für jede Quelle höchstens // eine Regel gibt. if schonDa then continue; // Andernfalls müssen wir schauen, ob es zu dem Ziel / den Zielen // der Quelle schon eine Regel gibt. schonDa:=false; setlength(tmpZiele,0); findeMehrZiele(tmpZiele,_oriAbh[i].zieleFkt,_dats[j].name,machDatei,_dats); if length(tmpZiele)=0 then begin gibAus('Fehler: Keine Ziele für Abhängigkeit!',3); gibAus('Quelle:',3); gibAus(''''+_dats[j].name+'''',3); gibAus('Ziele:',3); for k:=0 to _oriAbh[i].zieleFkt.count-1 do gibAus(' '''+_oriAbh[i].zieleFkt[k]+'''',3); fehler('... ich beende.'); end; for k:=0 to _oriAbh[i].erben.count-1 do if gleicheDateinamen(_oriAbh[i].erben[k].ziele,tmpZiele) then begin // es gibt schon eine Regel für die Ziele der Quelle schonDa:=true; // dann wird dieser Regel nur die Quelle hinzugefügt setlength(_oriAbh[i].erben[k].quellen,length(_oriAbh[i].erben[k].quellen)+1); _oriAbh[i].erben[k].quellen[length(_oriAbh[i].erben[k].quellen)-1]:=_dats[j]; // und womöglich die befehle generiert for l:=0 to _oriAbh[i].befehle.count-1 do begin s:=_oriAbh[i].befehle[l]; if quellersetzung(s,_dats[j].name,machDatei) then begin if l=0 then _oriAbh[i].erben[k].befehle.clear; _oriAbh[i].erben[k].befehle.add(s); end; end; neues:=true; break; // mehr Ziele sollte es ohnehin nicht geben end; if schonDa then continue; // Ziel(e) ist/sind neu _mglAbh.add(tAbhaengigkeit.create); _oriAbh[i].erben.add(_mglAbh.last); setlength(_mglAbh.last.quellen,1); _mglAbh.last.quellen[0]:=_dats[j]; _mglAbh.last.zieleFkt:=_oriAbh[i].zieleFkt; setlength(_mglAbh.last.ziele,length(tmpZiele)); for k:=0 to length(tmpZiele)-1 do _mglAbh.last.ziele[k]:=tmpZiele[k]; _mglAbh.last.befehle.clear; for k:=0 to _oriAbh[i].befehle.count-1 do begin s:=_oriAbh[i].befehle[k]; if quellersetzung(s,_dats[j].name,machDatei) then _mglAbh.last.befehle.add(s); end; _mglAbh.last.prioritaet:=_oriAbh[i].prioritaet; end; until not neues; end; procedure tMach.findeWasZuTunIst; var i,j,k: longint; ziA,quA: tAktualitaet; begin // schauen, welche Regeln angewandt werden müssen for i:=0 to _mglAbh.count-1 do begin for j:=0 to length(_mglAbh[i].quellen)-1 do findeMehrZiele(_mglAbh[i].ziele,_mglAbh[i].zieleFkt,_mglAbh[i].quellen[j].name,machDatei,_dats); if length(_mglAbh[i].ziele)=0 then begin // immer noch keine ziele gibAus('Warnung: Keine ziele für diese Abhängigkeit!',3); gibAus('quellen:',3); for k:=0 to length(_mglAbh[i].quellen)-1 do gibAus(' '''+_mglAbh[i].quellen[k].name+'''',3); gibAus('zieleFkt:',3); for k:=0 to _mglAbh[i].zieleFkt.count-1 do gibAus(' '''+_mglAbh[i].zieleFkt[k]+'''',3); gibAus(' - wird ignoriert.',3); continue; end; quA:=aAktuell; for j:=0 to length(_mglAbh[i].quellen)-1 do if _mglAbh[i].quellen[j].aktuell<>aAktuell then quA:=aWirdErneuert; ziA:=aWirdErneuert; for j:=0 to length(_mglAbh[i].ziele)-1 do ziA:=min(ziA,_mglAbh[i].ziele[j].aktuell); if ziA=0 do begin for i:=0 to _ztAbh.count-1 do if _ztAbh[i].prioritaet=prior then for j:=0 to _ztAbh[i].befehle.count-1 do befehle.add(_ztAbh[i].befehle[j]); dec(prior); end; if not unsicher then begin lokTest:=tRegExpr.create; if extractfilepath(ausgabeDatei)=extractfilepath(pruefsummenDatei) then lokTest.expression:='^'+extractfilepath(ausgabeDatei)+'/' else lokTest.expression:='^('+extractfilepath(ausgabeDatei)+'|'+extractfilepath(pruefsummenDatei)+')/'; for i:=0 to befehle.count-1 do testeObBefehlLokal(befehle[i],extractfiledir(ausgabeDatei),lokTest); lokTest.free; end; for i:=geaenderteDateien.count-1 downto 0 do begin if geaenderteDateien[i]='.uralt.' then begin geaenderteDateien.delete(i); continue; end; for j:=0 to i-1 do if geaenderteDateien[i]=geaenderteDateien[j] then begin geaenderteDateien.delete(i); break; end; end; if geaenderteDateien.count>0 then begin befehle.add('echo -ne "Sha512summen erneuern ..."'); befehl:='sed "'; for i:=0 to geaenderteDateien.count-1 do befehl:=befehl+'/\s'+escape(geaenderteDateien[i],'./','\')+'\$/d; '; befehl:=befehl+'" -i "'+pruefsummenDatei+'"'; befehle.add(befehl); for i:=0 to geaenderteDateien.count-1 do befehle.add('/usr/bin/sha512sum "'+geaenderteDateien[i]+'" >> '+pruefsummenDatei); befehle.add('echo " fertig"'); end; if _ztAbh.count=0 then befehle.add('echo "Es gibt hier nichts zu tun!"'); if ausgabeDatei='' then begin writeln('befehle:'); for i:=0 to befehle.count-1 do writeln(befehle[i]); end else begin assignFile(ausg,ausgabeDatei); rewrite(ausg); for i:=0 to befehle.count-1 do writeln(ausg,'('+befehle[i]+') || (read -p "Ein Fehler ist aufgetreten! ... "; exit 1)'); closeFile(ausg); end; befehle.free; geaenderteDateien.free; end; procedure tMach.setzeGenerischePruefsummenDatei; begin pruefsummenDatei:=extractfilepath(machDatei)+'.summen'; end; function tMach.anzOriAbh: longint; begin result:=_oriAbh.count; end; function tMach.anzMglAbh: longint; begin result:=_mglAbh.count; end; function tMach.anzZtAbh: longint; begin result:=_ztAbh.count; end; function tMach.anzDats: longint; begin result:=length(_dats); end; // allgemeine Funktionen ******************************************************* function quellersetzung(var worin: string; quelle, inputfile: string): boolean; var s,anfang,mitte: string; i,li,re: longint; begin result:=true; if quelle<>'' then begin while pos('%in',worin)>0 do worin:=copy(worin,1,pos('%in',worin)-1)+quelle+copy(worin,pos('%in',worin)+3,length(worin)); s:=extractfilename(quelle); while pos('%ifile',worin)>0 do worin:=copy(worin,1,pos('%ifile',worin)-1)+s+copy(worin,pos('%ifile',worin)+6,length(worin)); if pos('.',s)>0 then begin while s[length(s)]<>'.' do delete(s,length(s),1); delete(s,length(s),1); end; while pos('%basename',worin)>0 do worin:=copy(worin,1,pos('%basename',worin)-1)+s+copy(worin,pos('%basename',worin)+9,length(worin)); while pos('%dirname(',worin)>0 do begin anfang:=erstesArgument(worin,'%dirname('); mitte:=erstesArgument(worin,')'); s:=extractfilepath(quelle); if rightStr(s,1)='/' then delete(s,length(s),1); i:=anzCs('/',s)+1; li:=strtoint(erstesArgument(mitte,',')); if li<0 then li:=li+i; if mitte='' then re:=0 else re:=strtoint(mitte); if re<=0 then re:=re+i; while li>0 do begin dec(li); dec(re); erstesArgument(s,'/'); end; mitte:=''; while re>0 do begin dec(re); mitte:=mitte+'/'+erstesArgument(s,'/'); end; delete(mitte,1,1); worin:=anfang+mitte+worin; end; s:=extractfilepath(quelle); if rightStr(s,1)='/' then delete(s,length(s),1); while pos('%dirname',worin)>0 do worin:=copy(worin,1,pos('%dirname',worin)-1)+s+copy(worin,pos('%dirname',worin)+8,length(worin)); end; s:=extractfilepath(inputfile); if rightStr(s,1)='/' then delete(s,length(s),1); while pos('%DIRNAME',worin)>0 do worin:=copy(worin,1,pos('%DIRNAME',worin)-1)+s+copy(worin,pos('%DIRNAME',worin)+8,length(worin)); while pos('%num''',worin)>0 do begin anfang:=erstesArgument(worin,'%num'''); mitte:=erstesArgument(worin,''''); for i:=length(mitte) downto 1 do if mitte[i] in ['0'..'9'] then break else delete(mitte,i,1); for i:=length(mitte) downto 1 do if not (mitte[i] in ['0'..'9']) then begin delete(mitte,1,i); break; end; worin:=anfang+mitte+worin; end; if quelle<>'' then begin result:=false; while pos('%nurmit''',worin)>0 do begin anfang:=erstesArgument(worin,'%nurmit'''); mitte:=erstesArgument(worin,''''); if pos(mitte,quelle)=0 then exit; worin:=anfang+worin; end; while pos('%nurohne''',worin)>0 do begin anfang:=erstesArgument(worin,'%nurohne'''); mitte:=erstesArgument(worin,''''); if pos(mitte,quelle)>0 then exit; worin:=anfang+worin; end; result:=true; end; end; procedure sammleDateien(wo: string; rekursiv: boolean; ignoriere: array of tRegExpr; var dats: tDateienMitDaten); var sr: tSearchRec; err,i: longint; weglassen: boolean; begin err:=FindFirst(wo,fareadOnly or faHidden or faSysFile or (byte(rekursiv)*faDirectory),sr); while err=0 do begin weglassen:=false; for i:=0 to length(ignoriere)-1 do weglassen:=weglassen or ignoriere[i].exec(extractfilepath(wo)+sr.name); if not weglassen then begin if sr.attr and faDirectory <> 0 then begin if rekursiv and (sr.name<>'.') and (sr.name<>'..') then sammleDateien(extractfilepath(wo)+sr.name+'/*',rekursiv,ignoriere,dats); end else begin setlength(dats,length(dats)+1); dats[length(dats)-1]:=tDateiMitDatum.create; dats[length(dats)-1].name:=extractfilepath(wo)+sr.name; dats[length(dats)-1].aktuell:=aVeraltet; end; end; err:=FindNext(sr); end; findClose(sr); end; procedure zieleHinzufuegen(var ziele: tDateienMitDaten; ziel,quelle,inputfile: string; var dats: tDateienMitDaten); var i,ebene,ende: longint; s,t,u: string; wasda: boolean; begin if pos('{',ziel)=0 then begin if not quellersetzung(ziel,quelle,inputfile) then exit; ziel:=unescape(ziel); i:=0; wasda:=false; while i0) or (ziel[ende]<>'}') do begin inc(ende); case ziel[ende] of '{': inc(ebene); '}': dec(ebene); end{of case}; end; s:=copy(ziel,1,pos('{',ziel)-1); t:=copy(ziel,pos('{',ziel)+1,ende-pos('{',ziel)-1); u:=copy(ziel,ende+1,length(ziel)); if unescapedpos('..',t)>0 then begin for i:=strtoint(copy(t,1,unescapedpos('..',t)-1)) to strtoint(copy(t,unescapedpos('..',t)+2,length(t))) do zieleHinzufuegen(ziele,s+inttostr(i)+u,quelle,inputfile,dats); end else begin t:=t+','; while unescapedpos(',',t)>0 do begin zieleHinzufuegen(ziele,s+copy(t,1,unescapedpos(',',t)-1)+u,quelle,inputfile,dats); delete(t,1,unescapedpos(',',t)); end; end; end; end; procedure findeMehrZiele(var ziele: tDateienMitDaten; zieleFkt: tStringList; quelle,inputfile: string; var dats: tDateienMitDaten); var i: longint; begin for i:=0 to zieleFkt.count-1 do zieleHinzufuegen(ziele,zieleFkt[i],quelle,inputfile,dats); end; end.