diff --git a/.gitattributes b/.gitattributes
index 8e2ca490de..7b855a3783 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -6317,6 +6317,10 @@ utils/simulator/mm64.pas svneol=native#text/plain
utils/simulator/simbase.pas svneol=native#text/plain
utils/simulator/simlib.pas svneol=native#text/plain
utils/svn2cl.pp svneol=native#text/plain
+utils/svn2cvs/svn2cvs.lpi svneol=native#text/plain
+utils/svn2cvs/svn2cvs.pp svneol=native#text/plain
+utils/svn2cvs/test.xml svneol=native#text/plain
+utils/svn2cvs/vers.pp svneol=native#text/plain
utils/tply/COPYING -text
utils/tply/Makefile -text
utils/tply/Makefile.fpc svneol=native#text/plain
diff --git a/utils/svn2cvs/svn2cvs.lpi b/utils/svn2cvs/svn2cvs.lpi
new file mode 100644
index 0000000000..82ece449e3
--- /dev/null
+++ b/utils/svn2cvs/svn2cvs.lpi
@@ -0,0 +1,193 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/utils/svn2cvs/svn2cvs.pp b/utils/svn2cvs/svn2cvs.pp
new file mode 100644
index 0000000000..6c7586e12e
--- /dev/null
+++ b/utils/svn2cvs/svn2cvs.pp
@@ -0,0 +1,521 @@
+{$mode objfpc}
+{$H+}
+program svn2cvs;
+
+uses Classes,sysutils,process,DOM,xmlread,custapp,IniFiles;
+
+Const
+ SGlobal = 'Global';
+ KeyCVSBin = 'CVSBinary';
+ KeySVNBin = 'SVNBinary';
+ KeySVNURL = 'SVNURL';
+ KeyCVSROOT = 'CVSROOT';
+ KeyRepository = 'CVSRepository';
+ KeyRevision = 'Revision';
+ KeyWorkDir = 'WorkingDir';
+
+Resourcestring
+ SErrFailedToCheckOut = 'Failed to check out SVN repository';
+ SErrFailedToInitCVS = 'Failed to initialize CVS: ';
+ SErrNoRepository = 'Cannot initialize CVS: no CVS Repository specified';
+ SErrDirectoryFailed = 'Failed to create directory : %s';
+ SErrFailedToGetVersions = 'Failed to retrieve SVN versions';
+ SErrInValidSVNLog = 'Invalid SVN log.';
+ SErrUpdateFailed = 'Update to revision %d failed.';
+ SErrFailedToCommit = 'Failed to commit to CVS.';
+ SErrFailedToRemove = 'Failed to remove file: %s';
+ SErrFailedToAddDirectory = 'Failed to add directory to CVS: %s';
+ SErrFailedToAddFile = 'Failed to add file to CVS: %s';
+ SErrDirectoryNotInCVS = 'Directory not in CVS: %s';
+ SLogRevision = 'Revision %s by %s :';
+ SConvertingRevision = 'Converting revision : %d';
+ SWarnUnknownAction = 'Warning: Unknown action: "%s" for filename : "%s"';
+ SWarnErrorInLine = 'Warning: Erroneous file line : %s';
+ SExecuting = 'Executing: %s';
+
+Type
+
+ { TSVN2CVSApp }
+ TVersion = Class(TCollectionItem)
+ private
+ FAuthor: String;
+ FDate: string;
+ FLogMessage: String;
+ FRevision: Integer;
+ Public
+ Property Revision : Integer read FRevision;
+ Property LogMessage : String Read FLogMessage;
+ Property Date : string Read FDate;
+ Property Author : String Read FAuthor;
+ end;
+
+ { TVersions }
+
+ TVersions = Class(TCollection)
+ private
+ function GetVersion(Index : INteger): TVersion;
+ procedure SetVersion(Index : INteger; const AValue: TVersion);
+ Protected
+ procedure ConvertLogEntry(E : TDomElement);
+ public
+ Procedure LoadFromXML(Doc : TXMlDocument);
+ property Versions[Index : INteger] : TVersion Read GetVersion Write SetVersion; Default;
+ end;
+
+ { TSVN2CVSApp }
+
+ TSVN2CVSApp = Class(TCustomApplication)
+ Public
+ SVNBin : String;
+ CVSBin : String;
+ versions : TVersions;
+ WorkingDir : String;
+ StartRevision : Integer;
+ SVNURL : String;
+ CVSROOT : String;
+ CVSRepository : String;
+ Function RunCmd(Cmd: String; CmdOutput: TStream): Boolean;
+ Function RunSVN(Cmd : String; CmdOutput : TStream) : Boolean;
+ Function RunCVS(Cmd : String; CmdOutput : TStream) : Boolean;
+ Function UpdateSVN(Version : TVersion; Files : TStrings) : Boolean;
+ Procedure WriteLogMessage(Version : TVersion);
+ Procedure UpdateEntry(AFileName : String);
+ Procedure DeleteEntry(AFileName : String);
+ Procedure DoCVSEntries(Version : TVersion;Files : TStrings);
+ procedure CheckInCVS;
+ procedure CheckOutSVN(Files : TStrings);
+ Procedure ConvertVersion(Version : TVersion);
+ Procedure ConvertRepository;
+ procedure GetVersions;
+ procedure ProcessConfigFile;
+ Function ProcessArguments : Boolean;
+ Procedure DoRun; override;
+ end;
+
+ AppError = Class(Exception);
+
+{ TVersions }
+
+function TVersions.GetVersion(Index : INteger): TVersion;
+begin
+ Result:=Items[Index] as Tversion;
+end;
+
+procedure TVersions.SetVersion(Index : INteger; const AValue: TVersion);
+begin
+ Items[Index]:=AValue;
+end;
+
+procedure TVersions.ConvertLogEntry(E : TDomElement);
+
+ Function GetNodeText(N : TDomNode) : String;
+
+ begin
+ N:=N.FirstChild;
+ If N<>Nil then
+ Result:=N.NodeValue;
+ end;
+
+Var
+ N : TDomNode;
+ V : TVersion;
+
+begin
+ V:=Add as TVersion;
+ V.FRevision:=StrToIntDef(E['revision'],-1);
+ N:=E.FirstChild;
+ While (N<>Nil) do
+ begin
+ If (N.NodeType=ELEMENT_NODE) then
+ begin
+ if (N.NodeName='author') then
+ V.FAuthor:=GetNodeText(N)
+ else If (N.NodeName='date') then
+ V.FDate:=GetNodeText(N)
+ else If (N.NodeName='msg') then
+ V.FLogMessage:=GetNodeText(N);
+ end;
+ N:=N.NextSibling;
+ end;
+end;
+
+procedure TVersions.LoadFromXML(Doc: TXMlDocument);
+
+var
+ L : TDomNode;
+ E : TDomElement;
+
+begin
+ L:=Doc.FirstChild;
+ While (L<>Nil) and not ((L.NodeType=ELEMENT_NODE) and (L.NodeName='log')) do
+ L:=L.NextSibling;
+ if (L=Nil) then
+ Raise AppError.Create(SErrInValidSVNLog);
+ L:=L.FirstChild;
+ While (L<>Nil) do
+ begin
+ If (L.NodeType=ELEMENT_NODE) and (L.NodeName='logentry') then
+ E:=TDomElement(L);
+ ConvertLogEntry(E);
+ L:=L.NextSibling;
+ end;
+end;
+
+
+{ TSVN2CVSApp }
+
+function TSVN2CVSApp.RunCmd(Cmd: String; CmdOutput: TStream): Boolean;
+
+Var
+ Buf : Array[1..4096] of Byte;
+ Count : Integer;
+
+begin
+ With TProcess.Create(Self) do
+ Try
+ CommandLine:=cmd;
+ Writeln(Format(SExecuting,[CommandLine]));
+ if (CmdOutput<>Nil) then
+ Options:=[poUsePipes];
+ Execute;
+ If (CmdOutPut=Nil) then
+ WaitOnExit
+ else
+ Repeat
+ Count:=Output.Read(Buf,SizeOf(Buf));
+ If (Count>0) then
+ cmdOutput.Write(Buf,Count);
+ Until (Count=0);
+ Result:=(ExitStatus=0);
+ finally
+ Free;
+ end;
+end;
+
+function TSVN2CVSApp.RunSVN(Cmd: String; CmdOutput: TStream): Boolean;
+
+
+begin
+ Result:=RunCmd(SVNbin+' '+Cmd,CmdOutput);
+end;
+
+function TSVN2CVSApp.RunCVS(Cmd: String; CmdOutput: TStream): Boolean;
+begin
+ Result:=RunCmd(CVSbin+' '+Cmd,CmdOutput);
+end;
+
+procedure TSVN2CVSApp.CheckOutSVN(Files : TStrings);
+
+Var
+ S : TStringStream;
+
+begin
+ S:=TStringStream.Create('');
+ Try
+ if not RunSVN(Format('co -r %d %s .',[StartRevision,SVNURL]),S) then
+ Raise AppError.Create(SErrFailedToCheckOut);
+ Files.Text:=S.DataString;
+ Finally
+ FreeAndNil(S);
+ end;
+end;
+
+procedure TSVN2CVSApp.CheckInCVS;
+
+Var
+ F : Text;
+
+begin
+ If not ForceDirectories(WorkingDir+'CVS') then
+ Try
+ AssignFile(F,WorkingDir+'CVS/Root');
+ Rewrite(F);
+ Try
+ Writeln(F,CVSRoot);
+ Finally
+ CloseFile(F);
+ end;
+ AssignFile(F,WorkingDir+'CVS/Repository');
+ Rewrite(F);
+ Try
+ Writeln(F,CVSRepository);
+ Finally
+ Close(F);
+ end;
+ AssignFile(F,WorkingDir+'CVS/Entries');
+ Rewrite(F);
+ Try
+ // Do nothing.
+ Finally
+ Close(F);
+ end;
+ except
+ On E : Exception do
+ begin
+ E.Message:=SErrFailedToInitCVS+E.Message;
+ Raise;
+ end;
+ end;
+end;
+
+procedure TSVN2CVSApp.Convertrepository;
+
+Var
+ InitCVS,INITSVN : Boolean;
+ I : Integer;
+ Files : TStringList;
+
+begin
+ If Not DirectoryExists(WorkingDir) then
+ begin
+ if Not ForceDirectories(WorkingDir) then
+ Raise AppError.CreateFmt(SErrDirectoryFailed,[WorkingDir]);
+ InitSVN:=True;
+ InitCVS:=true;
+ end
+ else
+ begin
+ if Not DirectoryExists(WorkingDir+'.svn') then
+ InitSVN:=True;
+ if Not DirectoryExists(WorkingDir+'CVS') then
+ InitCVS:=True;
+ end;
+ ChDir(WorkingDir);
+ if InitCVS and (CVSRepository='') then
+ Raise AppError.Create(SErrNoRepository);
+ if InitSVN then
+ begin
+ Files:=TStringList.Create;
+ Try
+ CheckoutSVN(Files);
+ if InitCVS then
+ begin
+ CheckinCVS;
+ DoCVSEntries(Nil,Files);
+ end
+ else
+ DoCVSEntries(Nil,Files);
+ finally
+ FreeAndNil(Files);
+ end;
+ end;
+ GetVersions;
+ For I:=0 to Versions.Count-1 do
+ ConvertVersion(Versions[i]);
+end;
+
+procedure TSVN2CVSApp.GetVersions;
+
+Var
+ S : TStringStream;
+ Doc : TXMLDocument;
+
+begin
+ Versions:=TVersions.Create(TVersion);
+ S:=TStringStream.Create('');
+ Try
+ if not RunSVN(Format('log --xml -r %d:HEAD',[StartRevision]),S) then
+ Raise AppError(SErrFailedToGetVersions);
+ S.Position:=0;
+ ReadXMLFile(Doc,S);
+ Try
+ Versions.LoadFromXML(Doc);
+ finally
+ Doc.Free;
+ end;
+ Finally
+ S.Free;
+ end;
+end;
+
+
+procedure TSVN2CVSApp.ConvertVersion(Version: TVersion);
+
+Var
+ Files : TStringList;
+
+begin
+ Writeln(Format(SConvertingRevision,[Version.revision]));
+ Files:=TStringList.Create;
+ Try
+ If Not UpdateSVN(Version,Files) then
+ Raise AppError.CreateFmt(SErrUpdateFailed,[Version.Revision]);
+ DoCVSEntries(Version,Files);
+ Finally
+ Files.Free;
+ end;
+end;
+
+Function TSVN2CVSApp.UpdateSVN(Version : TVersion; Files : TStrings) : Boolean;
+
+Var
+ S : TStringStream;
+
+begin
+ S:=TStringStream.Create('');
+ Try
+ Result:=RunSVN(Format('up -r %d',[version.revision]),S);
+ if Result then
+ Files.Text:=S.DataString;
+ Finally
+ S.Free;
+ end;
+end;
+
+Procedure TSVN2CVSApp.WriteLogMessage(Version : TVersion);
+
+Var
+ F : Text;
+
+begin
+ AssignFile(F,'logmsg.txt');
+ Rewrite(F);
+ Try
+ Writeln(F,Format(SLogRevision,[Version.Revision,Version.Author]));
+ Writeln(F, Version.LogMessage);
+ Finally
+ CloseFile(F);
+ end;
+end;
+
+Procedure TSVN2CVSApp.DoCVSEntries(Version : TVersion;Files : TStrings);
+
+Var
+ I,P : Integer;
+ Action : Char;
+ FileName : String;
+
+begin
+ For I:=0 to Files.Count-1 do
+ begin
+ FileName:=trim(Files[i]);
+ P:=Pos(' ',FileName);
+ if (P=0) then
+ Writeln(StdErr,Format(SWarnErrorInLine,[FileName]))
+ else
+ begin
+ Action:=FileName[1];
+ system.Delete(FileName,1,P);
+ FileName:=Trim(FileName);
+ end;
+ Case UpCase(action) of
+ 'U' : UpdateEntry(FileName);
+ 'D' : DeleteEntry(FileName);
+ else
+ Writeln(stdErr,Format(SWarnUnknownAction,[Action,FileName]));
+ end;
+ end;
+ WriteLogMessage(version);
+ Try
+ If not RunCVS('commit -m -F logmsg.txt .',Nil) then
+ Raise AppError.Create(SErrFailedToCommit);
+ Finally
+ if not DeleteFile('logmsg.txt') then
+ Writeln(StdErr,'Warning: failed to remove log message file.');
+ end;
+end;
+
+Procedure TSVN2CVSApp.UpdateEntry(AFileName : String);
+
+Var
+ FD : String;
+ L : TStringList;
+ I : Integer;
+ Found : Boolean;
+
+begin
+ If ((FileGetAttr(AFileName) and faDirectory)<>0) then
+ begin
+ if Not RunCVS('add '+AFileName,Nil) then
+ Raise AppError.CreateFmt(SErrFailedToAddDirectory,[AFileName]);
+ end
+ else // Check if file is under CVS control by checking the Entries file.
+ begin
+ FD:=ExtractFilePath(AFileName);
+ If not DirectoryExists(FD+'Entries') then
+ Raise AppError.CreateFmt(SErrDirectoryNotInCVS,[FD]);
+ Found:=False;
+ L:=TStringList.Create;
+ Try
+ L.LoadFromFile(FD+'Entries');
+ Found:=False;
+ I:=0;
+ While (not found) and (I'') and (CVSROOT<>'');
+ If Result then
+ begin
+ If (WorkingDir='') then
+ WorkingDir:=GetCurrentDir;
+ WorkingDir:=IncludeTrailingPathDelimiter(WorkingDir);
+ end;
+end;
+
+begin
+ With TSVN2CVSApp.Create(Nil) do
+ try
+ Initialize;
+ Run;
+ Finally
+ free;
+ end;
+end.
diff --git a/utils/svn2cvs/test.xml b/utils/svn2cvs/test.xml
new file mode 100644
index 0000000000..06eb22a7cc
--- /dev/null
+++ b/utils/svn2cvs/test.xml
@@ -0,0 +1,86 @@
+
+
+
+fpc
+2005-05-21T09:42:41.620737Z
+ * log and id tags removed
+
+
+
+florian
+2005-05-19T22:16:53.958853Z
+ * fixed comparisation of booleans and nulls in variants, fixes bug 3953
+
+
+
+florian
+2005-05-19T22:13:11.823700Z
+ * createguid fixed
+
+
+
+michael
+2005-05-19T21:14:45.797276Z
++ Removed VER1_0 defines
+
+
+michael
+2005-05-19T17:31:25.033833Z
++ Implementation of CreateGUID
+
+
+fpc
+2005-05-18T20:24:09.513140Z
+ * property svn:mime-type for most files in main branch fixed
+
+
+
+fpc
+2005-05-18T16:53:52.841566Z
+ * eol style property in main branch fixed
+
+
+
+fpc
+2005-05-18T16:16:11.495319Z
+ * property svn:eol-style: native set
+
+
+
+marco
+2005-05-18T08:57:17.758875Z
+ * Patch from maillist for read() on a file with only a few numerical digits
+ in them and no crlf
+
+
+
+florian
+2005-05-17T22:27:53.833452Z
+* format(%u",[<qword>]); fixed
+* made test working
+
+
+peter
+2005-05-16T20:59:02.681395Z
+ * post 2.0.0 fixes from cvs
+
+
+
+
+fpc
+2005-05-16T18:37:41.817974Z
+initial import
+
+
diff --git a/utils/svn2cvs/vers.pp b/utils/svn2cvs/vers.pp
new file mode 100644
index 0000000000..1fdcaaf8ff
--- /dev/null
+++ b/utils/svn2cvs/vers.pp
@@ -0,0 +1,134 @@
+{$mode objfpc}
+{$h+}
+program vers;
+
+uses Classes,sysutils,process,DOM,xmlread,custapp,IniFiles;
+
+Type
+ { TVersion }
+ TVersion = Class(TCollectionItem)
+ private
+ FAuthor: String;
+ FDate: string;
+ FLogMessage: String;
+ FRevision: Integer;
+ Public
+ Property Revision : Integer read FRevision;
+ Property LogMessage : String Read FLogMessage;
+ Property Date : string Read FDate;
+ Property Author : String Read FAuthor;
+ end;
+
+ { TVersions }
+
+ TVersions = Class(TCollection)
+ private
+ function GetVersion(Index : INteger): TVersion;
+ procedure SetVersion(Index : INteger; const AValue: TVersion);
+ Protected
+ procedure ConvertLogEntry(E : TDomElement);
+ public
+ Procedure LoadFromXML(Doc : TXMlDocument);
+ property Versions[Index : INteger] : TVersion Read GetVersion Write SetVersion; Default;
+ end;
+
+ AppError = Class(Exception);
+
+Resourcestring
+ SErrInValidSVNLog = 'INvalid SVN log';
+
+{ TVersions }
+
+function TVersions.GetVersion(Index : INteger): TVersion;
+begin
+ Result:=Items[Index] as Tversion;
+end;
+
+procedure TVersions.SetVersion(Index : INteger; const AValue: TVersion);
+begin
+ Items[Index]:=AValue;
+end;
+
+procedure TVersions.ConvertLogEntry(E : TDomElement);
+
+ Function GetNodeText(N : TDomNode) : String;
+
+ begin
+ N:=N.FirstChild;
+ If N<>Nil then
+ Result:=N.NodeValue;
+ end;
+
+Var
+ N : TDomNode;
+ V : TVersion;
+
+begin
+ V:=Add as TVersion;
+ V.FRevision:=StrToIntDef(E['revision'],-1);
+ N:=E.FirstChild;
+ While (N<>Nil) do
+ begin
+ If (N.NodeType=ELEMENT_NODE) then
+ begin
+ if (N.NodeName='author') then
+ V.FAuthor:=GetNodeText(N)
+ else If (N.NodeName='date') then
+ V.FDate:=GetNodeText(N)
+ else If (N.NodeName='msg') then
+ V.FLogMessage:=GetNodeText(N);
+ end;
+ N:=N.NextSibling;
+ end;
+end;
+
+procedure TVersions.LoadFromXML(Doc: TXMlDocument);
+
+var
+ L : TDomNode;
+ E : TDomElement;
+
+begin
+ L:=Doc.FirstChild;
+ While (L<>Nil) and not ((L.NodeType=ELEMENT_NODE) and (L.NodeName='log')) do
+ L:=L.NextSibling;
+ if (L=Nil) then
+ Raise AppError.Create(SErrInValidSVNLog);
+ L:=L.FirstChild;
+ While (L<>Nil) do
+ begin
+ If (L.NodeType=ELEMENT_NODE) and (L.NodeName='logentry') then
+ E:=TDomElement(L);
+ ConvertLogEntry(E);
+ L:=L.NextSibling;
+ end;
+end;
+
+Var
+ Doc : TXMLDocument;
+ F : TFileStream;
+ I : Integer;
+
+begin
+ With TVersions.Create(TVersion) do
+ Try
+ F:=TFileStream.Create('test.xml',fmOpenRead);
+ Try
+ ReadXMLFile(Doc,F);
+ Writeln('Got ',Count,' revisions');
+ LoadFromXml(Doc);
+ For I:=0 to count-1 do
+ begin
+ Writeln('Revision ',I,' : ');
+ Writeln('Revision : ',Versions[i].Revision);
+ Writeln('Author : ',Versions[i].Author);
+ Writeln('Date : ',Versions[i].Date);
+ Writeln('Message : ',Versions[i].LogMessage);
+ end;
+ finally
+ F.Free;
+ end;
+ Finally
+ Free;
+ end;
+end.