(* execute.sml
 *
 * Sample code for CMSC 32102 Project-1
 *
 * This is an "bullet-proof" implementation of Unix.execute.
 *)

structure Execute : sig

    exception CommandNotFound

    val execute : (string * string list) -> Unix.proc

  end = struct

    structure P = OS.Path
    structure FSys = OS.FileSys

    exception CommandNotFound

  (* check that we have read and execute permission for cmd and that it
   * is not a directory.
   *)
    fun isProg cmd = FSys.access(cmd, [FSys.A_READ, FSys.A_EXEC])
	  andalso not(FSys.isDir cmd)

    fun execute (cmd, args) = let
	  val fullCmd = (case P.splitDirFile cmd
		 of {dir="", file} => let
		    (* get the user's path *)
		      val path = (case OS.Process.getEnv "PATH"
			     of NONE => raise CommandNotFound
			      | SOME path => path
			    (* end case *))
		    (* check to see if file is an executable file in the list
		     * of directories.
		     *)
		      fun find [] = raise CommandNotFound
			| find (dir::r) = let
			    val cmd = P.joinDirFile{dir=dir, file=file}
			    in
			      if isProg cmd
				then cmd
				else find r
			    end
		      in
			find (String.tokens (fn #":" => true | _ => false) path)
		      end
		  | _ => if isProg cmd
		      then cmd
		      else raise CommandNotFound
		(* end case *))
	  in
	    Unix.execute (fullCmd, args)
	  end

  end
