#+build linux, windows, darwin, netbsd, openbsd, freebsd, haiku
package posix

import "core:c"

when ODIN_OS == .Windows {
	foreign import lib "system:libucrt.lib"
} else when ODIN_OS == .Darwin {
	foreign import lib "system:System"
} else {
	foreign import lib "system:c"
}

// unistd.h - standard symbolic constants and types

foreign lib {
	/*
	Checks the file named by the pathname pointed to by the path argument for
	accessibility according to the bit pattern contained in amode. 

	Example:
		if (posix.access("/tmp/myfile", posix.F_OK) != .OK) {
			fmt.printfln("/tmp/myfile access check failed: %v", posix.strerror(posix.errno()))
		}

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html ]]
	*/
	@(link_name=LACCESS)
	access :: proc(path: cstring, amode: Mode_Flags = F_OK) -> result ---

	/*
	Causes the directory named by path to become the current working directory.

	Example:
		if (posix.chdir("/tmp") == .OK) {
			fmt.println("changed current directory to /tmp")
		}

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html ]]
	*/
	@(link_name=LCHDIR)
	chdir :: proc(path: cstring) -> result ---

	/*
	Exits but, shall not call functions registered with atexit() nor any registered signal handlers.
	Open streams shall not be flushed.
	Whether open streams are closed (without flushing) is implementation-defined. Finally, the calling process shall be terminated with the consequences described below.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html ]]
	*/
	_exit :: proc(status: c.int) -> ! ---

	/*
	Places an absolute pathname of the current working directory into buf.

	Returns: buf as a cstring on success, nil (setting errno) on failure

	Example:
		size: int
		path_max := posix.pathconf(".", ._PATH_MAX)
		if path_max == -1 {
			size = 1024
		} else if path_max > 10240 {
			size = 10240
		} else {
			size = int(path_max)
		}

		buf: [dynamic]byte
		cwd: cstring
		for ; cwd == nil; size *= 2 {
			if err := resize(&buf, size); err != nil {
				fmt.panicf("allocation failure: %v", err)
			}

			cwd = posix.getcwd(raw_data(buf), len(buf))
			if cwd == nil {
				errno := posix.errno()
				if errno != .ERANGE {
					fmt.panicf("getcwd failure: %v", posix.strerror(errno))
				}
			}
		}

		fmt.println(path_max, cwd)

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html ]]
	*/
	@(link_name=LGETCWD)
	getcwd :: proc(buf: [^]c.char, size: c.size_t) -> cstring ---

	/*
	Remove an (empty) directory.

	]] More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html ]]
	*/
	@(link_name=LRMDIR)
	rmdir :: proc(path: cstring) -> result ---

	/*
	Copy nbyte bytes, from src, to dest, exchanging adjecent bytes.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/swab.html ]]
	*/
	@(link_name=LSWAB)
	swab :: proc(src: [^]byte, dest: [^]byte, nbytes: c.ssize_t) ---

	/*
	Remove a directory entry.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]]
	*/
	@(link_name=LUNLINK)
	unlink :: proc(path: cstring) -> result ---
}

when ODIN_OS == .Windows {
	@(private) LACCESS :: "_access"
	@(private) LCHDIR  :: "_chdir"
	@(private) LGETCWD :: "_getcwd"
	@(private) LRMDIR  :: "_rmdir"
	@(private) LSWAB   :: "_swab"
	@(private) LUNLINK :: "_unlink"
} else {
	@(private) LACCESS :: "access"
	@(private) LCHDIR  :: "chdir"
	@(private) LGETCWD :: "getcwd"
	@(private) LRMDIR  :: "rmdir"
	@(private) LSWAB   :: "swab"
	@(private) LUNLINK :: "unlink"
}

STDERR_FILENO :: 2
STDIN_FILENO  :: 0
STDOUT_FILENO :: 1

Mode_Flag_Bits :: enum c.int {
	X_OK = log2(X_OK),
	W_OK = log2(W_OK),
	R_OK = log2(R_OK),
}
Mode_Flags :: bit_set[Mode_Flag_Bits; c.int]

#assert(_F_OK == 0)
F_OK :: Mode_Flags{}

when ODIN_OS == .Windows {
	_F_OK :: 0
	X_OK  :: 1
	W_OK  :: 2
	R_OK  :: 4
	#assert(W_OK|R_OK == 6)
}
