12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031 |
- MODULE SVNAdmin; (** AUTHOR "rstoll"; *)
- (*
- .svn directories are per default write protected. Changing file properties is only supported in the Win32 version
- by kernel calls.
- *)
- IMPORT
- SVNOutput, SVNUtil,
- Strings, Dates,
- Commands, Files, Kernel32, KernelLog;
- CONST
- EntryFileFormat* = 8; (* expected svn version *)
- TYPE
- EntryEntity* = RECORD
- Format* : LONGINT; (* 1 *)
- Name* : ARRAY 256 OF CHAR; (* 2 *)
- NodeKind* : ARRAY 32 OF CHAR; (* 3 *)
- Revision* : LONGINT; (* 4 *)
- GlobalRemoval* : BOOLEAN;
- Url*, UrlConst* : ARRAY 256 OF CHAR; (* 5 *)
- RepositoryRoot* : ARRAY 256 OF CHAR; (* 6 *)
- Schedule* : ARRAY 32 OF CHAR; (* 7 *)
- TextLastUpdated* : ARRAY 32 OF CHAR; (* 8 *)
- Checksum* : ARRAY 33 OF CHAR; (* 9 *)
- LastChangedDate* : ARRAY 32 OF CHAR; (* 10: len ?= 28? *)
- LastChangedRevision* : LONGINT; (* 11 *)
- LastChangedAuthor* : ARRAY 256 OF CHAR; (* 12 *)
- Props* : ARRAY 256 OF CHAR; (* 13: maybe longer? *)
- (*PropsMods : ARRAY 256 OF CHAR; (* 14: maybe longer *)*)
- RepositoryUUID* : ARRAY 37 OF CHAR; (* 27 *)
- VersionUrl* : ARRAY 256 OF CHAR;
- END;
- TYPE
- TraverseHandler* = PROCEDURE {DELEGATE} ( CONST path : ARRAY OF CHAR; fileEntry : EntryEntity; data : ANY ) : BOOLEAN;
- TYPE
- Entry* = OBJECT
- VAR
- adminDir : EntryEntity;
- path : ARRAY 256 OF CHAR;
- name : ARRAY 32 OF CHAR;
- context : Commands.Context;
- fAdmin, fAdminTemp : Files.File;
- entriesfile, entriesfiletemp, svnpath : ARRAY 256 OF CHAR;
- pathIsFile, readGlobalData, readFromTempfile : BOOLEAN;
- r : Files.Reader;
- w : Files.Writer;
- pathLength : LONGINT;
- PROCEDURE &Init* ( c: Commands.Context );
- BEGIN
- context := c;
- readGlobalData := FALSE;
- END Init;
- PROCEDURE ReadVersionUrl* ( CONST filename : ARRAY OF CHAR );
- VAR
- propsfile, p, n, tmp : ARRAY 256 OF CHAR;
- nextFileEntry : BOOLEAN;
- pf : Files.File;
- pr : Files.Reader;
- BEGIN
- IF fAdmin # NIL THEN
- IF pathIsFile THEN
- Files.SplitPath ( path, p, n );
- Files.JoinPath ( p, ".svn/all-wcprops", propsfile );
- ELSE
- Files.JoinPath ( path, ".svn/all-wcprops", propsfile );
- END;
- IF filename # "" THEN
- COPY ( filename, n );
- END;
- pf := Files.Old ( propsfile );
- ASSERT ( pf # NIL );
- Files.OpenReader ( pr, pf, 0 );
- ASSERT ( pr # NIL );
- IF pathIsFile OR (n # "") THEN (* search for the file *)
- nextFileEntry := FALSE;
- LOOP
- pr.Ln ( tmp );
- IF pr.res # Files.Ok THEN
- KernelLog.String ( "ERROR: didn't find entry in the .svn/all-wcprops file for: " );
- KernelLog.String ( p ); KernelLog.String ( " file: " ); KernelLog.String ( n );
- KernelLog.Ln;
- RETURN;
- END;
- IF nextFileEntry & (tmp = n) THEN EXIT END;
- nextFileEntry := (tmp = "END");
- END;
- END;
- (* get url *)
- pr.SkipLn; pr.SkipLn; pr.SkipLn;
- pr.Ln ( adminDir.VersionUrl );
- END;
- END ReadVersionUrl;
- PROCEDURE SetPath* ( CONST p : ARRAY OF CHAR; VAR res : WORD );
- BEGIN
- r := NIL;
- readGlobalData := FALSE;
- COPY ( p, path );
- AnalyzePath ( res );
- ASSERT ( (res = SVNOutput.ResOK) OR (res = SVNOutput.ResNOTVERSIONED) );
- END SetPath;
- PROCEDURE AnalyzePath ( VAR res : WORD );
- BEGIN
- pathIsFile := FALSE;
- res := SVNOutput.ResOK;
- IF (path = ".") OR (path = "") THEN
- fAdmin := Files.Old (".svn/entries");
- IF fAdmin = NIL THEN
- res := SVNOutput.ResNOTVERSIONED;
- RETURN;
- END;
- ELSE
- Files.JoinPath ( path, ".svn/entries", entriesfile );
- fAdmin := Files.Old ( entriesfile );
- IF fAdmin = NIL THEN (* our parameter was a file destination *)
- Files.SplitPath ( path, svnpath, name );
- Files.JoinPath ( svnpath, ".svn/entries", entriesfile );
- fAdmin := Files.Old ( entriesfile );
- IF fAdmin = NIL THEN
- res := SVNOutput.ResNOTVERSIONED;
- RETURN;
- END;
- pathIsFile := TRUE; (* search the file in the svn-entries file *)
- Files.JoinPath ( svnpath, ".svn/tmp/od-entries", entriesfiletemp );
- ELSE
- (* Kernel32.SetFileAttributes ( entriesfile, {} );*)
- Files.JoinPath ( path, ".svn/tmp/od-entries", entriesfiletemp );
- END;
- END;
- ASSERT ( fAdmin # NIL );
- END AnalyzePath;
- PROCEDURE CreateTempfile*;
- VAR
- res : WORD;
- BEGIN
- IF SVNUtil.FileExists ( entriesfiletemp ) THEN
- Files.Delete ( entriesfiletemp, res );
- ASSERT ( res = Files.Ok );
- END;
- fAdminTemp := Files.New ( entriesfiletemp );
- ReadFromTempfile ( TRUE );
- END CreateTempfile;
- PROCEDURE ReadFromTempfile* ( b : BOOLEAN );
- BEGIN
- IF b THEN
- ASSERT ( fAdmin # NIL );
- ASSERT ( fAdminTemp # NIL );
- Files.OpenWriter ( w, fAdminTemp, 0 );
- Files.OpenReader ( r, fAdmin, 0 );
- END;
- readFromTempfile := b;
- END ReadFromTempfile;
- PROCEDURE GetUrl* ( VAR url : ARRAY OF CHAR );
- BEGIN
- COPY ( adminDir.Url, url );
- END GetUrl;
- PROCEDURE GetRepo* ( VAR repos : ARRAY OF CHAR );
- BEGIN
- COPY ( adminDir.RepositoryRoot, repos );
- END GetRepo;
- PROCEDURE GetVersion* () : LONGINT;
- BEGIN
- IF ~pathIsFile THEN
- RETURN adminDir.Revision;
- ELSE
- RETURN adminDir.LastChangedRevision;
- END;
- END GetVersion;
- PROCEDURE SkipGlobalData;
- VAR
- temp : ARRAY 256 OF CHAR;
- start: ARRAY 2 OF CHAR;
- BEGIN
- (* IF ~readGlobalData THEN
- readGlobalData := TRUE;*)
- ASSERT ( fAdmin # NIL );
- NEW( r, fAdmin, 0 );
- start[0] := CHR(12); start[1] := 0X;
- REPEAT
- r.Ln ( temp );
- IF readFromTempfile THEN
- WriteString ( w, temp );
- END;
- UNTIL Strings.StartsWith2(start, temp);
- (* END;*)
- END SkipGlobalData;
- (* read all lines until we reach end of entry *)
- PROCEDURE SkipReaderToEOE;
- VAR
- temp : ARRAY 256 OF CHAR;
- start: ARRAY 2 OF CHAR;
- BEGIN
- ASSERT ( r # NIL );
- start[0] := CHR(12); start[1] := 0X;
- REPEAT
- r.Ln ( temp );
- IF r.res # Files.Ok THEN RETURN END;
- UNTIL Strings.StartsWith2(start, temp);
- END SkipReaderToEOE;
- PROCEDURE IsItemVersioned* ( CONST filename : ARRAY OF CHAR ) : BOOLEAN;
- VAR
- temp : ARRAY 256 OF CHAR;
- BEGIN
- (* global data hasn't been read, so we need to do it now *)
- SkipGlobalData;
- readGlobalData := FALSE;
- ASSERT ( r # NIL );
- LOOP
- r.Ln(temp); (* 2: read name of file *)
- IF r.res # Files.Ok THEN RETURN FALSE END;
- IF readFromTempfile THEN
- WriteString ( w, temp );
- END;
- Strings.TrimWS(temp);
- IF filename = temp THEN
- RETURN TRUE;
- ELSE
- IF readFromTempfile THEN
- ReadWriteToEOE;
- ELSE
- SkipReaderToEOE;
- END;
- IF r.res # Files.Ok THEN RETURN FALSE END;
- END;
- END;
- END IsItemVersioned;
- (* add everything in the directory recursively
- - path
- - name: name of the directory inside the path which will be added
- - addGlobal: usually the first
- *)
- PROCEDURE Add* ( CONST path : ARRAY OF CHAR; CONST name : ARRAY OF CHAR; addGlobal : BOOLEAN; VAR res : WORD );
- VAR
- file, entryfile, tmp, tmp2 : Files.FileName;
- res1: WORD; time, date, size : LONGINT;
- w : Files.Writer;
- flags : SET;
- enum : Files.Enumerator;
- urlRepo : Strings.String;
- BEGIN
- res := SVNOutput.ResOK;
- SetPath ( path, res1 );
- ASSERT ( res1 = SVNOutput.ResOK );
- ASSERT ( ~pathIsFile );
- IF IsItemVersioned ( name ) THEN
- res := SVNOutput.ResALREADYVERSIONED;
- RETURN;
- END;
- Files.JoinPath ( path, name, file );
- ASSERT ( ~SVNUtil.FileExists ( file ) ); (* 'name' must be a directory *)
- NEW ( enum );
- enum.Open ( file, {} ); (* get all files/dirs in the actual directory *)
- Files.JoinPath ( path, ".svn/entries", entryfile );
- IF addGlobal THEN
- pathLength := Strings.Length(path)+1;
- fAdmin := Files.Old ( entryfile );
- ASSERT ( fAdmin # NIL );
- RemoveFileAttribute2 ( entryfile, fAdmin );
- ReadData ( res1 );
- fAdmin := Files.Old ( entryfile ); (* needed? *)
- Files.OpenWriter ( w, fAdmin, fAdmin.Length() );
- WriteAddEntry ( w, name, FALSE );
- w.Update;
- SetFileAttribute2 ( entryfile, fAdmin );
- END;
- urlRepo := Strings.Substring2 ( pathLength, file );
- w := CreateDummy ( file, urlRepo^ );
- (* add all files/dirs in the actual directory *)
- WHILE enum.HasMoreEntries() DO
- IF enum.GetEntry ( tmp, flags, time, date, size ) THEN
- KernelLog.String ( " A " ); KernelLog.String ( tmp ); KernelLog.Ln;
- Files.SplitPath ( tmp, tmp, tmp2 );
- IF Files.Directory IN flags THEN
- Add ( file, tmp2, FALSE, res );
- ASSERT ( res = SVNOutput.ResOK );
- END;
- (* TODO check whether file is already versioned.... hmm can this even happen?? *)
- WriteAddEntry ( w, tmp2, ~(Files.Directory IN flags) );
- END;
- END;
- w.Update;
- SetFileAttribute2 ( entryfile, fAdmin );
- END Add;
- (* creates a dummy .svn entry; scheduled for adding *)
- PROCEDURE CreateDummy* ( CONST path, urlRepoDir : ARRAY OF CHAR ) : Files.Writer;
- VAR
- entryfile : Files.FileName;
- f : Files.File;
- w : Files.Writer;
- data : EntryEntity;
- BEGIN
- CreateDirectory ( path );
- Files.JoinPath ( path, ".svn/entries", entryfile );
- f := Files.New ( entryfile );
- Files.OpenWriter ( w, f, 0 );
- data.Schedule := "add";
- Files.JoinPath ( adminDir.Url, urlRepoDir, data.Url );
- COPY ( adminDir.RepositoryRoot, data.RepositoryRoot );
- Write ( w, data );
- Files.Register ( f );
- RETURN w;
- END CreateDummy;
- PROCEDURE ReadData* ( VAR res : WORD );
- VAR
- i : INTEGER;
- tmp : ARRAY 256 OF CHAR;
- start: ARRAY 2 OF CHAR;
- BEGIN
- res := SVNOutput.ResOK;
- readGlobalData := TRUE; (* now we read global data *)
- Files.OpenReader ( r, fAdmin, 0 );
- r.Int ( adminDir.Format, FALSE ); r.SkipLn; (* 1 *)
- IF adminDir.Format < EntryFileFormat THEN
- res := SVNOutput.ResCLIENTOLD;
- RETURN;
- END;
- r.SkipLn; (* 2: name...first entry is empty *)
- r.Ln ( adminDir.NodeKind ); (* 3 *)
- r.Ln ( tmp ); (* 4 *)
- IF tmp # "" THEN
- Strings.StrToInt ( tmp, adminDir.Revision ); (* 4 *)
- END;
- r.Ln ( adminDir.Url ); (* 5 *)
- COPY ( adminDir.Url, adminDir.UrlConst );
- r.Ln ( adminDir.RepositoryRoot ); (* 6 *)
- r.Ln ( adminDir.Schedule ); (* 7 *)
- adminDir.GlobalRemoval := (adminDir.Schedule = "delete");
- r.SkipLn; (* 8 *)
- r.SkipLn; (* 9 *)
- r.Ln ( adminDir.LastChangedDate ); (* 10 *)
- r.Ln ( tmp ); (* 11 *)
- IF tmp # "" THEN
- Strings.StrToInt ( tmp, adminDir.LastChangedRevision ); (* 11 *)
- END;
- r.Ln ( adminDir.LastChangedAuthor ); (* 12 *)
- r.SkipLn; (* 13 *)
- (* empty line?? *)
- r.SkipLn; (* 14 *)
- r.Ln ( adminDir.Props ); (* 15 *)
- r.Ln ( tmp ); (* 16 *)
- start[0] := CHR(12); start[1] := 0X;
- IF ~Strings.StartsWith2(start, tmp) THEN
- FOR i:=1 TO 10 DO
- r.SkipLn; (* 17-27 *)
- END;
- r.Ln ( adminDir.RepositoryUUID );
- r.SkipLn; (* ^L *)
- END;
- IF pathIsFile THEN (* search for more deatils in entries file... file or directory entry *)
- IF ~IsItemVersioned ( name ) THEN
- res := SVNOutput.ResNOTVERSIONED;
- ELSE
- (* found file entry... read data into adminDir *)
- ReadFileData ( name, res );
- END;
- END;
- END ReadData;
- PROCEDURE ReadFileData ( CONST name : ARRAY OF CHAR; VAR res : WORD );
- VAR
- tmp : ARRAY 256 OF CHAR;
- start: ARRAY 2 OF CHAR;
- BEGIN
- res := SVNOutput.ResOK;
- IF name = "" THEN
- r.Ln ( adminDir.Name ); (* 2*)
- ELSE
- COPY ( name, adminDir.Name );
- END;
- Strings.Concat ( adminDir.UrlConst, "/", adminDir.Url ); Strings.Append ( adminDir.Url, adminDir.Name );
- r.Ln ( adminDir.NodeKind ); (* 3*)
- IF r.Peek() # CHR(12) THEN
- r.Ln ( tmp ); (* 4*)
- IF tmp # "" THEN
- Strings.StrToInt ( tmp, adminDir.Revision );
- END;
- r.SkipLn; r.SkipLn; (* 5-6 *)
- r.Ln ( adminDir.Schedule ); (* 7 *)
- IF adminDir.Schedule = "" THEN
- r.Ln ( adminDir.TextLastUpdated ); (* 8 *)
- r.Ln ( adminDir.Checksum ); (* 9 *)
- r.Ln ( adminDir.LastChangedDate ); (* 10 *)
- r.Ln ( tmp ); (* 11 *)
- IF tmp # "" THEN
- Strings.StrToInt ( tmp, adminDir.LastChangedRevision );
- ELSE
- res := SVNOutput.ResNOTVERSIONED;
- RETURN;
- END;
- r.Ln ( adminDir.LastChangedAuthor ); (* 12 *)
- END;
- END;
- (* read all lines until we reach end of entry; including CHR(12) *)
- start[0] := CHR(12); start[1] := 0X;
- REPEAT
- r.Ln ( tmp );
- IF r.res # Files.Ok THEN RETURN END;
- UNTIL Strings.StartsWith2(start, tmp);
- END ReadFileData;
- PROCEDURE PrintData*;
- BEGIN
- context.out.String ("Path: "); context.out.String ( path ); context.out.Ln; context.out.Update;
- context.out.String ("URL: ");
- context.out.String (adminDir.Url);
- context.out.Ln;
- context.out.String ("Repository Root: ");
- context.out.String (adminDir.RepositoryRoot);
- context.out.Ln;
- context.out.String ("Repository UUID: ");
- context.out.String (adminDir.RepositoryUUID);
- context.out.Ln;
- context.out.String ("Revision: ");
- context.out.Int (adminDir.Revision, 0);
- context.out.Ln;
- context.out.String ("Node Kind: ");
- IF adminDir.NodeKind = "dir" THEN
- context.out.String ("directory");
- ELSE
- context.out.String (adminDir.NodeKind);
- END;
- context.out.Ln;
- context.out.String ( "Schedule: " );
- IF (adminDir.Schedule = "") OR (adminDir.Schedule = "normal") THEN
- context.out.String ( "normal" );
- ELSE
- context.out.String ( adminDir.Schedule );
- END;
- context.out.Ln;
- (* if we added a file the rest of the informations aren't important *)
- IF adminDir.Schedule = "add" THEN
- RETURN;
- END;
- context.out.String ("Last Changed Author: ");
- context.out.String (adminDir.LastChangedAuthor);
- context.out.Ln;
- context.out.String ("Last Changed Revision: ");
- context.out.Int (adminDir.LastChangedRevision, 0);
- context.out.Ln;
- context.out.String ("Last Changed Date: ");
- context.out.String (adminDir.LastChangedDate);
- context.out.Ln;
- IF adminDir.NodeKind = "file" THEN
- context.out.String ("Text Last Updated: ");
- context.out.String (adminDir.TextLastUpdated);
- context.out.Ln;
- context.out.String ("Checksum: ");
- context.out.String (adminDir.Checksum);
- context.out.Ln;
- END;
- context.out.Update;
- END PrintData;
- PROCEDURE ReadWriteLines* ( count : LONGINT );
- VAR
- i : LONGINT;
- tmp : ARRAY 256 OF CHAR;
- BEGIN
- FOR i := 1 TO count DO
- IF r.Peek() = CHR(12) THEN
- tmp := "";
- ELSE
- r.Ln ( tmp );
- END;
- ASSERT ( r.res = Files.Ok );
- w.String ( tmp ); w.Char ( 0AX );
- END;
- END ReadWriteLines;
- PROCEDURE ReadWriteLine* ( VAR str : ARRAY OF CHAR );
- BEGIN
- IF r.Peek() # CHR(12) THEN
- r.Ln ( str );
- ELSE
- str := "";
- END;
- w.String ( str );
- w.Char ( 0AX );
- END ReadWriteLine;
- PROCEDURE ReadWriteRest*;
- VAR
- tmp : ARRAY 256 OF CHAR;
- len : LONGINT;
- BEGIN
- REPEAT
- r.Bytes ( tmp, 0, LEN(tmp), len );
- w.Bytes ( tmp, 0, len );
- UNTIL len < LEN(tmp);
- END ReadWriteRest;
- PROCEDURE ReadWriteString* ( CONST str : ARRAY OF CHAR );
- BEGIN
- IF r.Peek() # CHR(12) THEN
- r.SkipLn;
- END;
- w.String ( str );
- w.Char ( 0AX );
- END ReadWriteString;
- (* EOE = end of entry CHR(12) *)
- PROCEDURE ReadWriteToEOE*;
- VAR
- tmp : ARRAY 256 OF CHAR; start: ARRAY 2 OF CHAR;
- BEGIN
- start[0] := CHR(12); start[1] := 0X;
- REPEAT
- r.Ln ( tmp );
- IF r.res # Files.Ok THEN RETURN END;
- w.String ( tmp ); w.Char ( 0AX );
- UNTIL Strings.StartsWith2(start, tmp);
- END ReadWriteToEOE;
- PROCEDURE IsEOF* () : BOOLEAN;
- VAR
- c : CHAR;
- BEGIN
- c := r.Peek();
- RETURN (r.res # Files.Ok) OR (c = 0X);
- END IsEOF;
- PROCEDURE WriteUpdate*;
- VAR
- res : WORD;
- overwrite : BOOLEAN;
- BEGIN
- w.Update;
- Files.Register ( fAdminTemp );
- overwrite := TRUE;
- RemoveFileAttribute ( entriesfile );
- Files.CopyFile ( entriesfiletemp, entriesfile, overwrite, res );
- SetFileAttribute ( entriesfile );
- ASSERT ( res = Files.Ok );
- END WriteUpdate;
- PROCEDURE ReadWriteEOE*;
- BEGIN
- SkipReaderToEOE;
- w.Char ( CHR(12) ); w.Char ( 0AX );
- END ReadWriteEOE;
- END Entry;
- PROCEDURE WriteAddEntry* ( w : Files.Writer; CONST name : ARRAY OF CHAR; file : BOOLEAN );
- VAR
- data : EntryEntity;
- BEGIN
- COPY ( name, data.Name );
- data.Schedule := "add";
- IF file THEN data.NodeKind := "file" ELSE data.NodeKind := "dir" END;
- Write ( w, data );
- END WriteAddEntry;
- PROCEDURE WriteString ( w : Files.Writer; CONST line : ARRAY OF CHAR );
- BEGIN
- w.String ( line );
- w.Char ( 0AX );
- END WriteString;
- PROCEDURE WriteInt ( w : Files.Writer; line : LONGINT );
- BEGIN
- IF line # 0 THEN
- w.Int ( line, 0 );
- END;
- w.Char ( 0AX );
- END WriteInt;
- PROCEDURE Write* ( w : Files.Writer; data : EntryEntity );
- VAR
- i : LONGINT;
- tmp : ARRAY 34 OF CHAR;
- BEGIN
- IF data.Name = "" THEN
- (* write the header section in .svn/entries *)
- WriteInt ( w, EntryFileFormat ); (* 1 *)
- w.Char ( 0AX ); (* 2 *)
- WriteString ( w, "dir" ); (* 3 *)
- IF data.Schedule = "add" THEN
- w.String ( "0" ); w.Char ( 0AX ); (* 4 *)
- ELSE
- WriteInt ( w, data.Revision ); (* 4 *)
- END;
- WriteString ( w, data.Url ); (* 5 *)
- WriteString ( w, data.RepositoryRoot ); (* 6 *)
- WriteString ( w, data.Schedule ); (* 7 *)
- w.Char ( 0AX ); (* 8 *)
- w.Char ( 0AX ); (* 9 *)
- WriteString ( w, data.LastChangedDate ); (* 10 *)
- WriteInt ( w, data.LastChangedRevision ); (* 11 *)
- WriteString ( w, data.LastChangedAuthor ); (* 12 *)
- WriteString ( w, data.Props ); (* 13 *)
- w.Char ( 0AX ); (* 14 *)
- w.String ( "svn:special svn:externals svn:needs-lock" ); w.Char ( 0AX ); (* 15 *)
- IF data.Schedule = "" THEN
- FOR i := 16 TO 26 DO w.Char ( 0AX ) END;
- WriteString ( w, data.RepositoryUUID ); (* 27 *)
- END;
- ELSIF data.NodeKind = "file" THEN
- (* write the file section in .svn/entries *)
- WriteString ( w, data.Name ); (* 2 *)
- w.String ( "file" ); w.Char ( 0AX ); (* 3 *)
- IF data.Schedule = "add" THEN
- w.String ( "0" ); w.Char ( 0AX ); (* 4 *)
- ELSE
- WriteInt ( w, data.Revision ); (* 4 *)
- END;
- w.Char ( 0AX ); (* 5 *)
- w.Char ( 0AX ); (* 6 *)
- WriteString ( w, data.Schedule ); (* 7 *)
- IF data.Schedule = "" THEN
- IF data.TextLastUpdated = "" THEN
- (* TODO is this precise enough? maybe we need to get the time from the file directly *)
- Strings.FormatDateTime ( SVNOutput.DateFormat, Dates.Now(), tmp );
- WriteString ( w, tmp ); (* 8 *)
- ELSE
- WriteString ( w, data.TextLastUpdated ); (* 8 *)
- END;
- WriteString ( w, data.Checksum ); (* 9 *)
- WriteString ( w, data.LastChangedDate ); (* 10 *)
- WriteInt ( w, data.LastChangedRevision ); (* 11 *)
- WriteString ( w, data.LastChangedAuthor ); (* 12 *)
- (* TODO more parameters.. like has-props.. add them if needed *)
- END;
- ELSE
- (* write the directory section in .svn/entries *)
- WriteString ( w, data.Name ); (* 2 *)
- w.String ( "dir" ); w.Char ( 0AX ); (* 3 *)
- IF data.Schedule # "" THEN
- w.Char ( 0AX ); (* 4 *)
- w.Char ( 0AX ); (* 5 *)
- w.Char ( 0AX ); (* 6 *)
- WriteString ( w, data.Schedule ); (* 7 *)
- END;
- END;
- w.Char ( CHR(12) ); w.Char ( 0AX );
- w.Update;
- END Write;
- (* write data to the .svn/all-wcprops file *)
- PROCEDURE WriteWCPROPS* ( CONST path, filename, verurl : ARRAY OF CHAR );
- CONST
- key = "svn:wc:ra_dav:version-url";
- VAR
- tmp,fstr,fstr2 : ARRAY 256 OF CHAR;
- read, len, keyLength, i : LONGINT; res: WORD;
- fr, fw : Files.File;
- r : Files.Reader;
- w : Files.Writer;
- overwrite, nextFileEntry, hasDirEntry : BOOLEAN;
- BEGIN
- keyLength := Strings.Length ( key );
- Files.JoinPath ( path, ".svn/all-wcprops", fstr );
- Files.JoinPath ( path, ".svn/tmp/od-all-wcprops", fstr2 );
- fw := Files.Old ( fstr );
- IF fw = NIL THEN
- fw := Files.New ( fstr );
- Files.Register ( fw );
- END;
- RemoveFileAttribute2 ( fstr, fw );
- overwrite := TRUE;
- Files.CopyFile ( fstr, fstr2, overwrite, res );
- ASSERT ( res = Files.Ok );
- fr := Files.Old ( fstr2 );
- ASSERT ( fr # NIL );
- Files.OpenWriter ( w, fw, 0 );
- Files.OpenReader ( r, fr, 0 );
- hasDirEntry := FALSE;
- IF filename # "" THEN
- (* we have a file, first search for it and then add it at the correct position *)
- nextFileEntry := FALSE;
- LOOP
- r.Ln ( tmp );
- IF r.res # Files.Ok THEN
- IF ~hasDirEntry THEN
- (* make dummy dir entry *)
- w.String ( "K " ); w.String ( "0" ); w.Char ( 0AX );
- w.String ( "" ); w.Char ( 0AX );
- w.String ( "V " ); w.String ( "0" ); w.Char ( 0AX );
- w.String ( "" ); w.Char ( 0AX );
- w.String ( "END" ); w.Char ( 0AX );
- END;
- (* didn't find the file..so we add a new one *)
- w.String ( filename ); w.Char ( 0AX );
- w.Update;
- EXIT;
- END;
- hasDirEntry := TRUE;
- w.String ( tmp ); w.Char ( 0AX );
- IF nextFileEntry & (tmp = filename) THEN EXIT END;
- nextFileEntry := (tmp = "END");
- END;
- END;
- (* directory entry comes at the beginning *)
- w.String ( "K " ); w.Int ( keyLength, 0 ); w.Char ( 0AX );
- w.String ( key ); w.Char ( 0AX );
- w.String ( "V " ); w.Int ( Strings.Length ( verurl ), 0 ); w.Char ( 0AX );
- w.String ( verurl ); w.Char ( 0AX );
- w.String ( "END" ); w.Char ( 0AX );
- (* search the start position of the remaining entries *)
- FOR i := 1 TO 5 DO
- r.SkipLn;
- END;
- (* copy the rest *)
- read := 0;
- LOOP
- r.Bytes ( tmp, read, LEN(tmp), len );
- w.Bytes ( tmp, read, len );
- INC ( read, len );
- IF len <= LEN(tmp) THEN EXIT END;
- END;
- w.Update;
- SetFileAttribute2 ( fstr, fw );
- END WriteWCPROPS;
- PROCEDURE CreateDirectory* ( CONST path : ARRAY OF CHAR );
- VAR
- tmp, tmp2, svndir : Files.FileName;
- res : WORD;
- f : Files.File;
- w : Files.Writer;
- BEGIN
- Files.JoinPath ( path, ".svn", svndir ); Files.CreateDirectory ( svndir, res ); ASSERT ( res = 0 );
- Files.JoinPath ( svndir, "prop-base", tmp ); Files.CreateDirectory ( tmp, res ); ASSERT ( res = 0 );
- Files.JoinPath ( svndir, "props", tmp ); Files.CreateDirectory ( tmp, res ); ASSERT ( res = 0 );
- Files.JoinPath ( svndir, "text-base", tmp ); Files.CreateDirectory ( tmp, res ); ASSERT ( res = 0 );
- Files.JoinPath ( svndir, "tmp", tmp ); Files.CreateDirectory ( tmp, res ); ASSERT ( res = 0 );
- Files.JoinPath ( tmp, "prop-base", tmp2 ); Files.CreateDirectory ( tmp2, res ); ASSERT ( res = 0 );
- Files.JoinPath ( tmp, "props", tmp2 ); Files.CreateDirectory ( tmp2, res ); ASSERT ( res = 0 );
- Files.JoinPath ( tmp, "text-base", tmp2 ); Files.CreateDirectory ( tmp2, res ); ASSERT ( res = 0 );
- Files.JoinPath ( svndir, "format", tmp );
- f := Files.New ( tmp );
- Files.OpenWriter ( w, f, 0 );
- w.Int ( EntryFileFormat, 0 );
- w.Char ( 0AX );
- w.Update;
- Files.Register ( f );
- SetFileAttribute ( tmp );
- END CreateDirectory;
- (* refactor: use SVNAdmin.Read* to get checksum.. *)
- PROCEDURE ReadChecksum* ( CONST file : ARRAY OF CHAR ) : Strings.String;
- VAR
- tmp, path, name : ARRAY 256 OF CHAR;
- nextFileEntry : BOOLEAN;
- f : Files.File;
- r : Files.Reader;
- i : LONGINT;
- s : Strings.String;
- start: ARRAY 2 OF CHAR;
- BEGIN
- NEW ( s, 34 );
- Files.SplitPath ( file, path, name );
- IF Strings.EndsWith ( "svn-base", file ) THEN
- Files.SplitPath ( path, path, tmp );
- Files.SplitPath ( path, path, tmp );
- Strings.Truncate ( name, Strings.Length ( name ) - Strings.Length ( ".svn-base" ) );
- END;
- Files.JoinPath ( path, ".svn/entries", tmp );
- f := Files.Old ( tmp );
- ASSERT ( f # NIL );
- Files.OpenReader ( r, f, 0 );
- nextFileEntry := FALSE;
- LOOP
- r.Ln ( tmp );
- IF r.res # Files.Ok THEN RETURN NIL END;
- IF nextFileEntry & (tmp = name) THEN
- FOR i := 1 TO 6 DO
- r.Ln ( tmp );
- ASSERT ( r.res = Files.Ok );
- END;
- r.Ln ( s^ );
- RETURN s;
- END;
- start[0] := CHR(12); start[1] := 0X;
- nextFileEntry := Strings.StartsWith ( start, 0, tmp );
- END;
- END ReadChecksum;
- PROCEDURE CheckChecksum* ( CONST file : ARRAY OF CHAR ) : BOOLEAN;
- VAR
- s, s2 : Strings.String;
- BEGIN
- s := SVNUtil.GetChecksum ( file );
- s2 := ReadChecksum ( file );
- RETURN s^ = s2^;
- END CheckChecksum;
- PROCEDURE Traverse* ( CONST path : ARRAY OF CHAR; handler : TraverseHandler; data : ANY; verurl : BOOLEAN; VAR res : WORD );
- VAR
- tmp, tmp2 : ARRAY 256 OF CHAR;
- adminEntry : Entry;
- BEGIN
- NEW ( adminEntry, NIL );
- adminEntry.SetPath ( path, res );
- IF res # SVNOutput.ResOK THEN RETURN END;
- adminEntry.ReadData ( res );
- IF res # SVNOutput.ResOK THEN RETURN END;
- IF verurl & (adminEntry.adminDir.Schedule = "") THEN
- adminEntry.ReadVersionUrl ( "" );
- END;
- IF adminEntry.pathIsFile THEN
- Files.SplitPath ( path, tmp, tmp2 );
- IF handler ( tmp,adminEntry.adminDir, data ) THEN END;
- RETURN;
- END;
- IF ~handler ( path, adminEntry.adminDir, data ) THEN
- RETURN;
- END;
- WHILE adminEntry.r.Peek() # 0X DO
- adminEntry.ReadFileData ( "", res );
- ASSERT ( res = SVNOutput.ResOK );
- IF adminEntry.adminDir.NodeKind = "dir" THEN
- Files.JoinPath ( path, adminEntry.adminDir.Name, tmp );
- Traverse ( tmp, handler, data, verurl, res );
- ELSE
- IF verurl & (adminEntry.adminDir.Schedule = "") THEN
- adminEntry.ReadVersionUrl ( adminEntry.adminDir.Name );
- END;
- IF ~handler ( path, adminEntry.adminDir, data ) THEN
- RETURN;
- END;
- END;
- END;
- END Traverse;
- PROCEDURE CopyToBaseFile* ( CONST file : ARRAY OF CHAR );
- VAR
- res : WORD;
- overwrite : BOOLEAN;
- dest, path, name : Files.FileName;
- BEGIN
- IF SVNUtil.FileExists ( file ) THEN
- overwrite := TRUE;
- Files.SplitPath ( file, path, name );
- Files.JoinPath ( path, ".svn/text-base", path );
- Files.JoinPath ( path, name, dest );
- Strings.Append ( dest, ".svn-base" );
- Kernel32.SetFileAttributes ( dest, {} );
- Files.CopyFile ( file, dest, overwrite, res );
- Kernel32.SetFileAttributes ( dest, {Files.ReadOnly} );
- ASSERT ( res = Files.Ok );
- END;
- END CopyToBaseFile;
- PROCEDURE SetFileAttribute* ( file : ARRAY OF CHAR );
- BEGIN
- Kernel32.SetFileAttributes ( file, {Files.ReadOnly} );
- END SetFileAttribute;
- PROCEDURE RemoveFileAttribute* ( file : ARRAY OF CHAR );
- BEGIN
- Kernel32.SetFileAttributes ( file, {} );
- END RemoveFileAttribute;
- PROCEDURE SetFileAttribute2* ( file : ARRAY OF CHAR; f : Files.File );
- BEGIN
- Kernel32.SetFileAttributes ( file, {Files.ReadOnly} );
- INCL ( f.flags, Files.ReadOnly );
- END SetFileAttribute2;
- PROCEDURE RemoveFileAttribute2* ( file : ARRAY OF CHAR; f : Files.File );
- BEGIN
- Kernel32.SetFileAttributes ( file, {} );
- EXCL ( f.flags, Files.ReadOnly );
- END RemoveFileAttribute2;
- END SVNAdmin.
|