MODULE SVNUtil; (** AUTHOR "rstoll"; *)

IMPORT
	Files, Strings, MD5,
	SVNOutput,
	Dates;

TYPE
	(* only look for one match *)
	FSItemSearch* = OBJECT ( Files.Enumerator )	(** not shareable *)
		VAR
			adding, found: BOOLEAN;
			name0 : Files.FileName;
			path, mask : Strings.String;
			flags0: SET;
			time0, date0, size0 : LONGINT;

		PROCEDURE &Init*;
		BEGIN
			found := FALSE;
			adding := FALSE;
			NEW ( path, 512 );
			NEW ( mask, 512 );
		END Init;

		PROCEDURE Exists* () : BOOLEAN;
		BEGIN
			RETURN found;
		END Exists;

		PROCEDURE FileExists* () : BOOLEAN;
		BEGIN
			RETURN found & (Files.Old ( mask^ ) # NIL);
		END FileExists;

		PROCEDURE Open(m: ARRAY OF CHAR; flags: SET);
		BEGIN
			(* remove path delimiter at the end *)
			IF Strings.EndsWith ( Files.PathDelimiter, m ) THEN
				m[Strings.Length(m)-1] := 0X;
			END;

			COPY ( m, mask^ );
			Files.SplitPath ( mask^, path^, name0 );
			Strings.Append ( path^, "/*" );
			found := FALSE;

			adding := TRUE;
			Open^ ( path^, flags ); (* since i can't call the internal enumerator.. use the old Open procedure *)
			adding := FALSE;
		END Open;

		PROCEDURE GetEntry(VAR name: ARRAY OF CHAR; VAR flags: SET; VAR time, date, size: LONGINT): BOOLEAN;
		BEGIN
			IF found THEN
				COPY ( name0, name );
				flags := flags0;
				time := time0;
				date := date0;
				size := size0;
			END;

			RETURN found;
		END GetEntry;

		(** For internal use only. *)

		PROCEDURE PutEntry(VAR name: ARRAY OF CHAR; flags: SET; time, date, size: LONGINT);
		BEGIN
			ASSERT(adding);

			IF ~found & (name = mask^) THEN
				name[0] := name[0];
				found := TRUE;
				flags := flags0;
				time := time0;
				date := date0;
				size := size0;
			END;
		END PutEntry;

	END FSItemSearch;




PROCEDURE GetChecksum* ( CONST file : ARRAY OF CHAR ) : Strings.String;
CONST
	bufSize = 512;
VAR
	read, len : LONGINT;
	tmp, res : Strings.String;
	md5context : MD5.Context;
	md5digest : MD5.Digest;

	r : Files.Reader;
	f : Files.File;
BEGIN
	NEW ( tmp, bufSize );
	NEW ( res, 35 );

	f := Files.Old ( file  );
	ASSERT ( f # NIL );

	Files.OpenReader ( r, f, 0 );

	md5context := MD5.New();
	read := 0;
	LOOP
		r.Bytes ( tmp^, read, bufSize, len );
		MD5.WriteBytes ( md5context, tmp^, len );
		IF len < bufSize THEN EXIT END;
	END;

	MD5.Close ( md5context, md5digest );
	MD5.ToString ( md5digest, res^ );

	RETURN res;
END GetChecksum;


PROCEDURE CheckChecksum* ( CONST file, checksum : ARRAY OF CHAR ) : BOOLEAN;
VAR
	s : Strings.String;
BEGIN
	s := GetChecksum ( file );
	RETURN s^ = checksum;
END CheckChecksum;




PROCEDURE GetUUID* () : Strings.String;
VAR
	md5context : MD5.Context;
	md5digest : MD5.Digest;
	tmp : Strings.String;
BEGIN
	NEW ( tmp, 40 );
	md5context := MD5.New();
	Strings.FormatDateTime ( SVNOutput.DateFormat, Dates.Now(), tmp^ );

	MD5.WriteBytes ( md5context, tmp^, Strings.Length ( tmp^ ) );
	MD5.Close ( md5context, md5digest );
	MD5.ToString ( md5digest, tmp^ );

	RETURN tmp;
END GetUUID;


PROCEDURE FileExists* ( CONST file : ARRAY OF CHAR ) : BOOLEAN;
BEGIN
	RETURN Files.Old ( file ) # NIL;
END FileExists;


(* simplistic url encoder/decoder stuff *)
PROCEDURE UrlEncode* ( CONST input : ARRAY OF CHAR; VAR output : ARRAY OF CHAR );
CONST
	unsafe = " <>#%[]|\^~[]'"; (* what about " *)
VAR
	i, j : LONGINT;
	tmp : ARRAY 3 OF CHAR;
BEGIN
	i := 0; j := 0;
	WHILE input[i] # 0X DO
		IF Strings.Find ( unsafe, 0, input[i] ) = -1 THEN
			output[j] := input[i];
			INC ( j );
		ELSE
			output[j] := '%'; INC ( j );
			Strings.IntToHexStr ( ORD(input[i]), 1, tmp );
			output[j] := tmp[0]; INC ( j );
			output[j] := tmp[1]; INC ( j );
		END;
		INC ( i );
	END;
	output[j] := 0X;
END UrlEncode;


PROCEDURE UrlDecode* (CONST input : ARRAY OF CHAR; VAR output : ARRAY OF CHAR );
VAR
	i, j, value, res : LONGINT;
	tmp : ARRAY 3 OF CHAR;
BEGIN
	i := 0; j := 0;
	tmp[2] := 0X;
	WHILE input[i] # 0X DO
		IF input[i] = '%' THEN
			INC ( i );
			tmp[0] := input[i]; INC ( i );
			tmp[1] := input [i]; INC ( i );

			Strings.HexStrToInt ( tmp, value, res ); ASSERT ( res = Strings.Ok );
			output[j] := CHR ( value ); INC ( j );
		ELSE
			output[j] := input[i];
			INC ( j ); INC ( i );
		END;
	END;
	output[j] := 0X;
END UrlDecode;



PROCEDURE RemoveFileDelimiterAtEnd* ( VAR s : ARRAY OF CHAR );
BEGIN
	IF Strings.EndsWith ( Files.PathDelimiter, s ) THEN
		s[Strings.Length(s)-1] := 0X;
	END;
END RemoveFileDelimiterAtEnd;


END SVNUtil.