MODULE SVNAdmin;
IMPORT
SVNOutput, SVNUtil,
Strings, Dates,
Commands, Files, Kernel32, KernelLog;
CONST
EntryFileFormat* = 8;
TYPE
EntryEntity* = RECORD
Format* : LONGINT;
Name* : ARRAY 256 OF CHAR;
NodeKind* : ARRAY 32 OF CHAR;
Revision* : LONGINT;
GlobalRemoval* : BOOLEAN;
Url*, UrlConst* : ARRAY 256 OF CHAR;
RepositoryRoot* : ARRAY 256 OF CHAR;
Schedule* : ARRAY 32 OF CHAR;
TextLastUpdated* : ARRAY 32 OF CHAR;
Checksum* : ARRAY 33 OF CHAR;
LastChangedDate* : ARRAY 32 OF CHAR;
LastChangedRevision* : LONGINT;
LastChangedAuthor* : ARRAY 256 OF CHAR;
Props* : ARRAY 256 OF CHAR;
RepositoryUUID* : ARRAY 37 OF CHAR;
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
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;
pr.SkipLn; pr.SkipLn; pr.SkipLn;
pr.Ln ( adminDir.VersionUrl );
END;
END ReadVersionUrl;
PROCEDURE SetPath* ( CONST p : ARRAY OF CHAR; VAR res : LONGINT );
BEGIN
r := NIL;
readGlobalData := FALSE;
COPY ( p, path );
AnalyzePath ( res );
ASSERT ( (res = SVNOutput.ResOK) OR (res = SVNOutput.ResNOTVERSIONED) );
END SetPath;
PROCEDURE AnalyzePath ( VAR res : LONGINT );
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
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;
Files.JoinPath ( svnpath, ".svn/tmp/od-entries", entriesfiletemp );
ELSE
Files.JoinPath ( path, ".svn/tmp/od-entries", entriesfiletemp );
END;
END;
ASSERT ( fAdmin # NIL );
END AnalyzePath;
PROCEDURE CreateTempfile*;
VAR
res : LONGINT;
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
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 SkipGlobalData;
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
SkipGlobalData;
readGlobalData := FALSE;
ASSERT ( r # NIL );
LOOP
r.Ln(temp);
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;
PROCEDURE Add* ( CONST path : ARRAY OF CHAR; CONST name : ARRAY OF CHAR; addGlobal : BOOLEAN; VAR res : LONGINT );
VAR
file, entryfile, tmp, tmp2 : Files.FileName;
res1, 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 ) );
NEW ( enum );
enum.Open ( file, {} );
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 );
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^ );
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;
WriteAddEntry ( w, tmp2, ~(Files.Directory IN flags) );
END;
END;
w.Update;
SetFileAttribute2 ( entryfile, fAdmin );
END Add;
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 : LONGINT );
VAR
i : INTEGER;
tmp : ARRAY 256 OF CHAR;
start: ARRAY 2 OF CHAR;
BEGIN
res := SVNOutput.ResOK;
readGlobalData := TRUE;
Files.OpenReader ( r, fAdmin, 0 );
r.Int ( adminDir.Format, FALSE ); r.SkipLn;
IF adminDir.Format < EntryFileFormat THEN
res := SVNOutput.ResCLIENTOLD;
RETURN;
END;
r.SkipLn;
r.Ln ( adminDir.NodeKind );
r.Ln ( tmp );
IF tmp # "" THEN
Strings.StrToInt ( tmp, adminDir.Revision );
END;
r.Ln ( adminDir.Url );
COPY ( adminDir.Url, adminDir.UrlConst );
r.Ln ( adminDir.RepositoryRoot );
r.Ln ( adminDir.Schedule );
adminDir.GlobalRemoval := (adminDir.Schedule = "delete");
r.SkipLn;
r.SkipLn;
r.Ln ( adminDir.LastChangedDate );
r.Ln ( tmp );
IF tmp # "" THEN
Strings.StrToInt ( tmp, adminDir.LastChangedRevision );
END;
r.Ln ( adminDir.LastChangedAuthor );
r.SkipLn;
r.SkipLn;
r.Ln ( adminDir.Props );
r.Ln ( tmp );
start[0] := CHR(12); start[1] := 0X;
IF ~Strings.StartsWith2(start, tmp) THEN
FOR i:=1 TO 10 DO
r.SkipLn;
END;
r.Ln ( adminDir.RepositoryUUID );
r.SkipLn;
END;
IF pathIsFile THEN
IF ~IsItemVersioned ( name ) THEN
res := SVNOutput.ResNOTVERSIONED;
ELSE
ReadFileData ( name, res );
END;
END;
END ReadData;
PROCEDURE ReadFileData ( CONST name : ARRAY OF CHAR; VAR res : LONGINT );
VAR
tmp : ARRAY 256 OF CHAR;
start: ARRAY 2 OF CHAR;
BEGIN
res := SVNOutput.ResOK;
IF name = "" THEN
r.Ln ( adminDir.Name );
ELSE
COPY ( name, adminDir.Name );
END;
Strings.Concat ( adminDir.UrlConst, "/", adminDir.Url ); Strings.Append ( adminDir.Url, adminDir.Name );
r.Ln ( adminDir.NodeKind );
IF r.Peek() # CHR(12) THEN
r.Ln ( tmp );
IF tmp # "" THEN
Strings.StrToInt ( tmp, adminDir.Revision );
END;
r.SkipLn; r.SkipLn;
r.Ln ( adminDir.Schedule );
IF adminDir.Schedule = "" THEN
r.Ln ( adminDir.TextLastUpdated );
r.Ln ( adminDir.Checksum );
r.Ln ( adminDir.LastChangedDate );
r.Ln ( tmp );
IF tmp # "" THEN
Strings.StrToInt ( tmp, adminDir.LastChangedRevision );
ELSE
res := SVNOutput.ResNOTVERSIONED;
RETURN;
END;
r.Ln ( adminDir.LastChangedAuthor );
END;
END;
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 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;
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 : LONGINT;
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
WriteInt ( w, EntryFileFormat );
w.Char ( 0AX );
WriteString ( w, "dir" );
IF data.Schedule = "add" THEN
w.String ( "0" ); w.Char ( 0AX );
ELSE
WriteInt ( w, data.Revision );
END;
WriteString ( w, data.Url );
WriteString ( w, data.RepositoryRoot );
WriteString ( w, data.Schedule );
w.Char ( 0AX );
w.Char ( 0AX );
WriteString ( w, data.LastChangedDate );
WriteInt ( w, data.LastChangedRevision );
WriteString ( w, data.LastChangedAuthor );
WriteString ( w, data.Props );
w.Char ( 0AX );
w.String ( "svn:special svn:externals svn:needs-lock" ); w.Char ( 0AX );
IF data.Schedule = "" THEN
FOR i := 16 TO 26 DO w.Char ( 0AX ) END;
WriteString ( w, data.RepositoryUUID );
END;
ELSIF data.NodeKind = "file" THEN
WriteString ( w, data.Name );
w.String ( "file" ); w.Char ( 0AX );
IF data.Schedule = "add" THEN
w.String ( "0" ); w.Char ( 0AX );
ELSE
WriteInt ( w, data.Revision );
END;
w.Char ( 0AX );
w.Char ( 0AX );
WriteString ( w, data.Schedule );
IF data.Schedule = "" THEN
IF data.TextLastUpdated = "" THEN
Strings.FormatDateTime ( SVNOutput.DateFormat, Dates.Now(), tmp );
WriteString ( w, tmp );
ELSE
WriteString ( w, data.TextLastUpdated );
END;
WriteString ( w, data.Checksum );
WriteString ( w, data.LastChangedDate );
WriteInt ( w, data.LastChangedRevision );
WriteString ( w, data.LastChangedAuthor );
END;
ELSE
WriteString ( w, data.Name );
w.String ( "dir" ); w.Char ( 0AX );
IF data.Schedule # "" THEN
w.Char ( 0AX );
w.Char ( 0AX );
w.Char ( 0AX );
WriteString ( w, data.Schedule );
END;
END;
w.Char ( CHR(12) ); w.Char ( 0AX );
w.Update;
END Write;
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, res : LONGINT;
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
nextFileEntry := FALSE;
LOOP
r.Ln ( tmp );
IF r.res # Files.Ok THEN
IF ~hasDirEntry THEN
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;
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;
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 );
FOR i := 1 TO 5 DO
r.SkipLn;
END;
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 : LONGINT;
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;
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 : LONGINT );
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 : LONGINT;
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.