diff -uNr a/vtools/manifest b/vtools/manifest --- a/vtools/manifest f0dd63123a8745028d31cb4db9674994382318cbdc7c613d9886bf55c1ceacd8b22a7a4d9573fcf09ef53363caf0942ce2bcb3183e04abd09be6981a38208b32 +++ b/vtools/manifest 71f7968fc2d5944b4a51a2c074ecbff5c07a11280b1eb5b3ff4ab9c76d1ebd34ce67e2b10e0a6cf580e65138d904df66a23520112c500cf3e15732b565418459 @@ -16,3 +16,5 @@ Vpatch tool support for "No newline at end of file" directive. 2018-09-26 phf Ksum standalone keccak hasher +2018-10-23 bvt +Standalone temporary file generation code. diff -uNr a/vtools/src/temporary_file.adb b/vtools/src/temporary_file.adb --- a/vtools/src/temporary_file.adb false +++ b/vtools/src/temporary_file.adb 4747969b6df8aa268419f3259a757d1fee33b8b4a7d440b8adfe07c7f6335060b533791908ff1f5022ecfb51708277a59b0876812c4343e17df1f3f0de0ea767 @@ -0,0 +1,71 @@ +with Bits; use Bits; +with System; use System; +with SMG_Keccak; use SMG_Keccak; + +package body Temporary_File is + -- Create a new file or exit when the file with specified name exists. + -- Use fopen(3) with "x" mode to create the file or error out if it + -- already exists. + subtype File_Ptr is System.Address; + subtype C_String is System.Address; + + function fopen (Path: C_String; + Mode: C_String) return File_Ptr; + pragma Import (C, fopen); + + procedure fclose (Stream: File_Ptr); + pragma Import (C, fclose); + + function Create_File_Exclusive(Path: String) return Boolean is + Fptr: File_Ptr; + begin + declare + XP : aliased String := Path & ASCII.NUL; + XM : aliased String := "wx" & ASCII.NUL; + begin + Fptr := fopen(XP'Address, XM'Address); + end; + if Fptr = System.Null_Address then + return False; + end if; + fclose(Fptr); + return True; + end Create_File_Exclusive; + + -- Create a temporary file with a randomly generated name; + -- if the file with random component F exists, retry with + -- H(F) as a new random component. + Function Temporary_File(Path_Prefix: String; + Seed: Bitstream) return String is + Hash: Bitstream(1..64*8); + Name_Ctx: Keccak_Context; + + procedure Hash_Bitstream(Input: Bitstream) is + begin + KeccakBegin(Name_Ctx); + KeccakHash(Name_Ctx, Input); + KeccakEnd(Name_Ctx, Hash); + end Hash_Bitstream; + begin + Hash_Bitstream(Seed); + loop + declare + File_Name: String := Path_Prefix & ToHex(Hash); + begin + if Create_File_Exclusive(File_Name) then + return File_Name; + end if; + end; + Hash_Bitstream(Hash); + end loop; + end Temporary_File; + + function Temporary_File(Path_Prefix: String; + Seed: String) return String is + B: Bitstream(1..Seed'Length*8); + begin + ToBitstream(Seed, B); + return Temporary_File(Path_Prefix, B); + end Temporary_File; + +end Temporary_File; diff -uNr a/vtools/src/temporary_file.ads b/vtools/src/temporary_file.ads --- a/vtools/src/temporary_file.ads false +++ b/vtools/src/temporary_file.ads 94160eed9e9d4daf0b80dea4cc22b8060554e972e84b072ef7b5227dbcde4f5b0367c0706a6885a00f1151a59039a94db196370dfcbe941e352ac8838ea3a03e @@ -0,0 +1,3 @@ +package Temporary_File is + function Temporary_File(Path_Prefix: String; Seed: String) return String; +end Temporary_File; diff -uNr a/vtools/src/vpatch.adb b/vtools/src/vpatch.adb --- a/vtools/src/vpatch.adb 548d5a8f4f97e8cbfe84822335672c485b6e713e0e1620f706464090e75a1850c059e681c0d35edd23c444cac55d8ecd5180a53f6b570c46c632c4c34a7bee99 +++ b/vtools/src/vpatch.adb 49e08a273d7b839abf0d9c08b4b8a8b054f20e96d82448758728a2d16de5074f6ca6bcd718c931fab4eed18104a20bd057b7aa46d8eb25ce539c7fd2a26e6408 @@ -1,6 +1,4 @@ with Bits; use Bits; -with Interfaces.C; -with Interfaces.C.Strings; with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; with Character_IO; use Character_IO; @@ -11,6 +9,7 @@ with Ada.Characters.Latin_1; with Ada.Sequential_IO; with SMG_Keccak; use SMG_Keccak; +with Temporary_File; use Temporary_File; procedure VPatch is package Latin_1 renames Ada.Characters.Latin_1; @@ -59,36 +58,22 @@ -- Temporary File - procedure MkTemp(Template: Interfaces.C.Strings.Chars_Ptr); - pragma Import(C, mktemp); - - function Temp_File_Name(Template: String) return String is - X: Interfaces.C.Strings.Chars_Ptr - := Interfaces.C.Strings.New_String(Template); - begin - MkTemp(X); - declare - Result: String := Interfaces.C.Strings.Value(X); - begin - Interfaces.C.Strings.Free(X); - return Result; - end; - end; - procedure Create_Temp(File : in out File_Type; Mode : in File_Mode := Out_File; - Template : in String := "vpatch.XXX"; + Prefix : in String; + Seed : in String := ""; Form : in String := "") is - Name: String := Temp_File_Name(Template); + Name: String := Temporary_File.Temporary_File(Prefix, Seed); begin Create(File, Mode, Name, Form); end; procedure Create_Temp(File : in out CIO.File_Type; Mode : in CIO.File_Mode := CIO.Out_File; - Template : in String := "vpatch.XXX"; + Prefix : in String; + Seed : in String := ""; Form : in String := "") is - Name: String := Temp_File_Name(Template); + Name: String := Temporary_File.Temporary_File(Prefix, Seed); begin Create(File, Mode, Name, Form); end; @@ -466,7 +451,7 @@ -- prepare keccak and open files KeccakBegin(To_Ctx); - Create_Temp(To_F, Template => "tmp.XXX"); + Create_Temp(To_F, Prefix => "/tmp/vpatch-", Seed => To_F_Name); case Op is when Op_Create => Has_Input_File := False; @@ -644,10 +629,11 @@ end if; case Op is when Op_Create | Op_Patch => - Dirs.Rename(Tmp_Name, To_F_Name); + Dirs.Copy_File(Tmp_Name, To_F_Name); when Op_Delete => - Dirs.Delete_File(Tmp_Name); + null; end case; + Dirs.Delete_File(Tmp_Name); end; exception