mkstemp

Stephen Weeks MLton@sourcelight.com
Thu, 21 Jun 2001 18:10:33 -0700


> My claim, given that the amount of extra functionality I am getting from
> mkstemp() above and beyond just using open() is pretty small, is that you
> do NOT want the suffix argument.  It just isn't useful very often.  I realize
> that in MLton it is exactly what you want, but that isn't enough of an
> argument in my opinion.  I vote for mkstemp being
> 	string -> string * TextIO.outstream
> and having MLton not use it for its temp files.  (The output only argument
> is similarly based on frequency of use.  In the old days tmp files were
> often used to save RAM between different parts of a program.  These days
> that isn't very likely, so mainly it is to communicate between different
> programs, and this means that what the creator wants is just the output
> side.)
> Remember, part of the argument, especially given the name, is to be close
> to the C version.  I would still not require the `XXXXXX' on the end of the
> string argument.  This is different, but things have to be a bit different
> here because the C version uses the fact that its string arg is mutable.

OK.  We will have both mkstemp and mkstemps.  The reason I object to just having
mkstemp and not mkstemps is that it is trivial to implement mkstemp given
mkstemps, but not vice versa.  And yes, the XXXXXX is definitely silly for
MLton.

Here is the final(?) implementation.

      val MLton.TextIO.mkstemp: string -> string * TextIO.outstream
      val MLton.TextIO.mkstemps: 
	{prefix: string, suffix: string} -> string * TextIO.outstream


      local
	 val chars =
	    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	 val n = Word.fromInt (String.size chars)
	 val r: word ref = ref 0w0
      in
	 fun tempName {prefix, suffix} =
	    let
	       val _ = r := Random.rand ()
	       val unique =
		  String.tabulate
		  (6, fn _ =>
		   let
		      val w = !r
		      val c = String.sub (chars,
					  Word.toInt (Word.mod (w, n)))
		      val _ = r := Word.div (w, n)
		   in c
		   end)
	    in
	       concat [prefix, unique, suffix]
	    end
      end

      fun mkstemps arg: string * outstream =
	 let
	    fun loop () =
	       let
		  val name = tempName arg
		  open Posix.FileSys
	       in
		  (name,
		   newOut (createf (name, O_WRONLY, O.flags [O.excl],
				    let open S
				    in flags [irusr, iwusr]
				    end)))
	       end handle e as PosixError.SysErr (_, SOME s) =>
		  if s = Posix.Error.exist
		     then loop ()
		  else raise e
	 in
	    loop ()
	 end

      fun mkstemp s = mkstemps {prefix = s, suffix = ""}