
proc {file copy} {{force {}} source target} {
	try {
		if {$force ni {{} -force}} {
			error "bad option \"$force\": should be -force"
		}

		if {![file exists $source]} {
			error "source file does not exist."
		}

		if {$force eq "" && [file exists $target]} {
			error "error copying \"$source\" to \"$target\": file already exists"
		}
		exec /mod/bin/busybox/cp $source $target
	} on error {msg opts} {
		incr opts(-level)
		return {*}$opts $msg
	}
}

proc {file rename} {{force {}} source target} {
	try {
		if {$force ni {{} -force}} {
			error "bad option \"$force\": should be -force"
		}

		if {![file exists $source]} {
			error "source file does not exist."
		}

		if {$force eq "" && [file exists $target]} {
			error "error copying \"$source\" to \"$target\": file already exists"
		}
		exec /mod/bin/busybox/mv $source $target
	} on error {msg opts} {
		incr opts(-level)
		return {*}$opts $msg
	}
}

proc {file touch} {target {ref ""}} {
	try {
		if {$ref ne ""} {
			if {![file exists $ref]} {
				error "ref file does not exist."
			}
			exec /mod/bin/busybox/touch -r $ref $target
		} else {
			exec /mod/bin/busybox/touch $target
		}
	} on error {msg opts} {
		incr opts(-level)
		return {*}$opts $msg
	}
}

proc {file tdelete} {target} {
	try {
		if {[file isdirectory $target]} {
			foreach f [readdir -nocomplain $target] {
				file tdelete "$target/$f"
			}
			exec /mod/bin/busybox/rm -rf $target
		} else {
			exec /mod/bin/trm $target
		}
	} on error {msg opts} {
		incr opts(-level)
		return {*}$opts $msg
	}
}

proc {file read} {target {bytes 0}} {
	try {
		if {[file readable $target]} {
			set fd [open $target]
			if {$bytes} {
				set ret [$fd read $bytes]
			} else {
				set ret [$fd read]
			}
			$fd close
			return $ret
		}
		error "Cannot read $target"
	} on error {msg opts} {
		incr opts(-level)
		return {*}$opts $msg
	}
}

proc {file write} {target content} {
	try {
		if {[catch {set fd [open $target w]} msg]} {
			error "Open failed, $msg"
		} else {
			$fd puts -nonewline $content
			$fd close
		}
	} on error {msg opts} {
		incr opts(-level)
		return {*}$opts $msg
	}
}

set ::fileops::locks {}

proc {file lock} {{create {}} target} {
	try {
		if {$create ni {{} -create}} {
			error "bad option \"$create\": should be -create"
		}
		if {[dict exists $::fileops::locks $target]} {
			return 0
		}
		if {$create eq "" && ![file exists $target]} {
			error "'$target' does not exist"
		} elseif {[catch {set fd [open $target w]} msg]} {
			error "open '$target' failed, $msg"
		} else {
			set ret [$fd lock]
			if {$ret == 1} {
				set ::fileops::locks($target) $fd
			} else {
				$fd close
			}
			return $ret
		}
	} on error {msg opts} {
		incr opts(-level)
		return {*}$opts $msg
	}
}

proc {file unlock} {target} {
	try {
		if {[dict exists $::fileops::locks $target]} {
			set fd $::fileops::locks($target)
			dict unset ::fileops::locks $target
			set ret [$fd unlock]
			$fd close
			return $ret
		}
		error "$target was not locked."
	} on error {msg opts} {
		incr opts(-level)
		return {*}$opts $msg
	}
}

local proc file {cmd args} {
	switch $cmd {
	    rename { tailcall {file rename} {*}$args }
	    copy { tailcall {file copy} {*}$args }
	    touch { tailcall {file touch} {*}$args }
	    tdelete { tailcall {file tdelete} {*}$args }
	    read { tailcall {file read} {*}$args }
	    write { tailcall {file write} {*}$args }
	    lock { tailcall {file lock} {*}$args }
	    unlock { tailcall {file unlock} {*}$args }
	    default { tailcall upcall file $cmd {*}$args }
	}
}

