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

import "base:intrinsics"

import "core:c"

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

// signal.h - signals

foreign lib {
	/*
	Raise a signal to the process/group specified by pid.

	If sig is 0, this function can be used to check if the pid is just checked for validity.

	If pid is -1, the signal is sent to all processes that the current process has permission to send.

	If pid is negative (not -1), the signal is sent to all processes in the group identifier by the
	absolute value.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html ]]
	*/
	kill :: proc(pid: pid_t, sig: Signal) -> result ---

	/*
	Shorthand for `kill(-pgrp, sig)` which will kill all processes in the given process group.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html ]]
	*/
	killpg :: proc(pgrp: pid_t, sig: Signal) -> result ---

	/*
	Writes a language-dependent message to stderror.

	Example:
		posix.psignal(.SIGSEGV, "that didn't go well")

	Possible Output:
		that didn't go well: Segmentation fault

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/psignal.html ]]
	*/
	psignal :: proc(signum: Signal, message: cstring) ---

	/*
	Send a signal to a thread.

	As with kill, if sig is 0, only validation (of the pthread_t given) is done and no signal is sent.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html ]]
	*/
	pthread_kill :: proc(thread: pthread_t, sig: Signal) -> Errno ---

	/*
	Examine and change blocked signals.

	Equivalent to sigprocmask(), without the restriction that the call be made in a single-threaded process.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html ]]
	*/
	pthread_sigmask :: proc(how: Sig, set: ^sigset_t, oset: ^sigset_t) -> Errno ---

	/*
	Examine and change blocked signals in a single-threaded process.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html ]]
	*/
	@(link_name=LSIGPROCMASK)
	sigprocmask :: proc(how: Sig, set: ^sigset_t, oldset: ^sigset_t) -> result ---

	/*
	Examine and change a signal action.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html ]]
	*/
	@(link_name=LSIGACTION)
	sigaction :: proc(sig: Signal, act: ^sigaction_t, oact: ^sigaction_t) -> result ---

	@(link_name=LSIGADDSET)
	sigaddset :: proc(set: ^sigset_t, signo: Signal) -> result ---
	@(link_name=LSIGDELSET)
	sigdelset :: proc(^sigset_t, Signal) -> c.int ---
	@(link_name=LSIGEMPTYSET)
	sigemptyset :: proc(^sigset_t) -> c.int ---
	@(link_name=LSIGFILLSET)
	sigfillset :: proc(^sigset_t) -> c.int ---

	/*
	Set and get the signal alternate stack context.

	Example:
		sigstk := posix.stack_t {
			ss_sp    = make([^]byte, posix.SIGSTKSZ) or_else panic("allocation failure"),
			ss_size  = posix.SIGSTKSZ,
			ss_flags = {},
		}
		if posix.sigaltstack(&sigstk, nil) != .OK {
			fmt.panicf("sigaltstack failure: %v", posix.strerror(posix.errno()))
		}

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaltstack.html ]]
	*/
	@(link_name=LSIGALTSTACK)
	sigaltstack :: proc(ss: ^stack_t, oss: ^stack_t) -> result ---

	/*
	Adds sig to the signal mask of the calling process.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]]
	*/
	sighold :: proc(sig: Signal) -> result ---

	/*
	Sets the disposition of sig to SIG_IGN.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]]
	*/
	sigignore :: proc(sig: Signal) -> result ---

	/*
	Removes sig from the signal mask of the calling process and suspend the calling process until
	a signal is received.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]]
	*/
	sigpause :: proc(sig: Signal) -> result ---

	/*
	Removes sig from the signal mask of the calling process.

	Returns: always -1.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]]
	*/
	sigrelse :: proc(sig: Signal) -> result ---

	/*
	Changes the restart behavior when a function is interrupted by the specified signal.

	If flag is true, SA_RESTART is removed, added otherwise.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/siginterrupt.html ]]
	*/
	siginterrupt :: proc(sig: Signal, flag: b32) -> result ---

	/*
	Test for a signal in a signal set.

	Returns: 1 if it is a member, 0 if not, -1 (setting errno) on failure

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigismember.html ]]
	*/
	@(link_name=LSIGISMEMBER)
	sigismember :: proc(set: ^sigset_t, signo: Signal) -> c.int ---

	/*
	Stores the set of signals that are blocked from delivery to the calling thread and that are pending
	on the process or the calling thread.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigpending.html ]]
	*/
	@(link_name=LSIGPENDING)
	sigpending :: proc(set: ^sigset_t) -> result ---

	/*
	Wait for one of the given signals.

	Returns: always -1

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsuspend.html ]]
	*/
	@(link_name=LSIGSUSPEND)
	sigsuspend :: proc(sigmask: ^sigset_t) -> result ---

	/*
	Wait for queued signals.

	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigwait.html ]]
	*/
	sigwait :: proc(set: ^sigset_t, sig: ^Signal) -> Errno ---

	/* NOTE: unimplemented on darwin.

	void   psiginfo(const siginfo_t *, const char *);
	int    sigqueue(pid_t, int, union sigval);
	void (*sigset(int, void (*)(int)))(int);
	int    sigsuspend(const sigset_t *);
	int    sigtimedwait(const sigset_t *restrict, siginfo_t *restrict,
	           const struct timespec *restrict);
	int    sigwaitinfo(const sigset_t *restrict, siginfo_t *restrict);
	*/
}

sigval :: struct #raw_union {
	sigval_int: c.int,  /* [PSX] integer signal value */
	sigval_ptr: rawptr, /* [PSX] pointer signal value */
}

ILL_Code :: enum c.int {
	// Illegal opcode.
	ILLOPC = ILL_ILLOPC,
	// Illegal operand.
	ILLOPN = ILL_ILLOPN,
	// Illegal addressing mode.
	ILLADR = ILL_ILLADR,
	// Illegal trap.
	ILLTRP = ILL_ILLTRP,
	// Priviledged opcode.
	PRVOPC = ILL_PRVOPC,
	// Priviledged register.
	PRVREG = ILL_PRVREG,
	// Coprocessor error.
	COPROC = ILL_COPROC,
	// Internal stack error.
	BADSTK = ILL_BADSTK,
}

FPE_Code :: enum c.int {
	// Integer divide by zero.
	INTDIV = FPE_INTDIV,
	// Integer overflow.
	INTOVF = FPE_INTOVF,
	// Floating-point divide by zero.
	FLTDIV = FPE_FLTDIV,
	// Floating-point overflow.
	FLTOVF = FPE_FLTOVF,
	// Floating-point underflow.
	FLTUND = FPE_FLTUND,
	// Floating-point inexact result.
	FLTRES = FPE_FLTRES,
	// Invalid floating-point operation.
	FLTINV = FPE_FLTINV,
	// Subscript out of range.
	FLTSUB = FPE_FLTSUB,
}

SEGV_Code :: enum c.int {
	// Address not mapped to object.
	MAPERR = SEGV_MAPERR,
	// Invalid permissions for mapped object.
	ACCERR = SEGV_ACCERR,
}

BUS_Code :: enum c.int {
	// Invalid address alignment.
	ADRALN = BUS_ADRALN,
	// Nonexistent physical address.
	ADRERR = BUS_ADRERR,
	// Object-specific hardware error.
	OBJERR = BUS_OBJERR,
}

TRAP_Code :: enum c.int {
	// Process breakpoint.
	BRKPT = TRAP_BRKPT,
	// Process trace trap.
	TRACE = TRAP_TRACE,
}

CLD_Code :: enum c.int {
	// Child has exited..
	EXITED    = CLD_EXITED,
	// Child has terminated abnormally and did not create a core file.
	KILLED    = CLD_KILLED,
	// Child has terminated abnormally and created a core file.
	DUMPED    = CLD_DUMPED,
	// Traced child trapped.
	TRAPPED   = CLD_TRAPPED,
	// Child has stopped.
	STOPPED   = CLD_STOPPED,
	// Stopped child has continued.
	CONTINUED = CLD_CONTINUED,
}

POLL_Code :: enum c.int {
	// Data input is available.
	IN  = POLL_IN,
	// Output buffers available.
	OUT = POLL_OUT,
	// Input message available.
	MSG = POLL_MSG,
	// I/O error.
	ERR = POLL_ERR,
	// High priority input available.
	PRI = POLL_PRI,
	// Device disconnected.
	HUP = POLL_HUP,
}

Any_Code :: enum c.int {
	// Signal sent by kill().
	USER    = SI_USER,
	// Signal sent by sigqueue().
	QUEUE   = SI_QUEUE,
	// Signal generated by expiration of a timer set by timer_settime().
	TIMER   = SI_TIMER,
	// Signal generated by completion of an asynchronous I/O request.
	ASYNCIO = SI_ASYNCIO,
	// Signal generated by arrival of a message on an empty message queue.
	MESGQ   = SI_MESGQ,
}

SA_Flags_Bits :: enum c.int {
	// Do not generate SIGCHLD when children stop or stopped children continue.
	NOCLDSTOP  = log2(SA_NOCLDSTOP),
	// Cause signal delivery to occur on an alternate stack.
	ONSTACK    = log2(SA_ONSTACK),
	// Cause signal disposition to be set to SIG_DFL on entry to signal handlers.
	RESETHAND  = log2(SA_RESETHAND),
	// Cause certain functions to become restartable.
	RESTART    = log2(SA_RESTART),
	// Cause extra information to be passed to signal handlers at the time of receipt of a signal.
	SIGINFO    = log2(SA_SIGINFO),
	// Cause implementation not to create zombie processes or status information on child termination.
	NOCLDWAIT  = log2(SA_NOCLDWAIT),
	// Cause signal not to be automatically blocked on entry to signal handler.
	SA_NODEFER = log2(SA_NODEFER),
}
SA_Flags :: bit_set[SA_Flags_Bits; c.int]

SS_Flag_Bits :: enum c.int {
	// Process is executing on an alternate signal stack.
	ONSTACK = log2(SS_ONSTACK),
	// Alternate signal stack is disabled.
	DISABLE = log2(SS_DISABLE),
}
SS_Flags :: bit_set[SS_Flag_Bits; c.int]

Sig :: enum c.int {
	// Resulting set is the union of the current set and the signal set and the complement of
	// the signal set pointed to by the argument.
	BLOCK   = SIG_BLOCK,
	// Resulting set is the intersection of the current set and the complement of the signal set
	// pointed to by the argument.
	UNBLOCK = SIG_UNBLOCK,
	// Resulting set is the signal set pointed to by the argument.
	SETMASK = SIG_SETMASK,
}

when ODIN_OS == .NetBSD {
	@(private) LSIGPROCMASK :: "__sigprocmask14"
	@(private) LSIGACTION   :: "__sigaction_siginfo"
	@(private) LSIGADDSET   :: "__sigaddset14"
	@(private) LSIGDELSET   :: "__sigdelset14"
	@(private) LSIGEMPTYSET :: "__sigemptyset14"
	@(private) LSIGFILLSET  :: "__sigfillset14"
	@(private) LSIGALTSTACK :: "__sigaltstack14"
	@(private) LSIGISMEMBER :: "__sigismember14"
	@(private) LSIGPENDING  :: "__sigpending14"
	@(private) LSIGSUSPEND  :: "__sigsuspend14"
} else {
	@(private) LSIGPROCMASK :: "sigprocmask"
	@(private) LSIGACTION   :: "sigaction"
	@(private) LSIGADDSET   :: "sigaddset"
	@(private) LSIGDELSET   :: "sigdelset"
	@(private) LSIGEMPTYSET :: "sigemptyset"
	@(private) LSIGFILLSET  :: "sigfillset"
	@(private) LSIGALTSTACK :: "sigaltstack"
	@(private) LSIGISMEMBER :: "sigismember"
	@(private) LSIGPENDING  :: "sigpending"
	@(private) LSIGSUSPEND  :: "sigsuspend"
}

when ODIN_OS == .Darwin {

	// Request that signal be held
	SIG_HOLD :: rawptr(uintptr(5))

	uid_t :: distinct c.uint32_t
	sigset_t :: distinct c.uint32_t

	SIGHUP    :: 1
	SIGQUIT   :: 3
	SIGTRAP   :: 5
	SIGPOLL   :: 7
	SIGKILL   :: 9
	SIGBUS    :: 10
	SIGSYS    :: 12
	SIGPIPE   :: 13
	SIGALRM   :: 14
	SIGURG    :: 16
	SIGCONT   :: 19
	SIGSTOP   :: 17
	SIGTSTP   :: 18
	SIGCHLD   :: 20
	SIGTTIN   :: 21
	SIGTTOU   :: 22
	SIGXCPU   :: 24
	SIGXFSZ   :: 25
	SIGVTALRM :: 26
	SIGPROF   :: 27
	SIGWINCH  :: 28
	SIGUSR1   :: 30
	SIGUSR2   :: 31

	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
	// `_t` has been added.

	sigaction_t :: struct {
		using _: struct #raw_union {
			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
		},
		sa_mask:  sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
		sa_flags: SA_Flags, /* [PSX] special flags */
	}

	SIG_BLOCK   :: 1
	SIG_UNBLOCK :: 2
	SIG_SETMASK :: 3

	SA_NOCLDSTOP :: 0x0008
	SA_ONSTACK   :: 0x0001
	SA_RESETHAND :: 0x0004
	SA_RESTART   :: 0x0002
	SA_SIGINFO   :: 0x0040
	SA_NOCLDWAIT :: 0x0020
	SA_NODEFER   :: 0x0010

	SS_ONSTACK :: 0x0001
	SS_DISABLE :: 0x0004

	MINSIGSTKSZ :: 32768
	SIGSTKSZ    :: 131072

	stack_t :: struct {
		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
		ss_size:  c.size_t, /* [PSX] stack size */
		ss_flags: SS_Flags, /* [PSX] flags */
	}

	siginfo_t :: struct {
		si_signo:  Signal, /* [PSX] signal number */
		si_errno:  Errno,  /* [PSX] errno value associated with this signal */
		si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
			ill:  ILL_Code,
			fpe:  FPE_Code,
			segv: SEGV_Code,
			bus:  BUS_Code,
			trap: TRAP_Code,
			chld: CLD_Code,
			poll: POLL_Code,
			any:  Any_Code,
		},
		si_pid:    pid_t,      /* [PSX] sending process ID */
		si_uid:    uid_t,      /* [PSX] real user ID of sending process */
		si_status: c.int,      /* [PSX] exit value or signal */
		si_addr:   rawptr,     /* [PSX] address of faulting instruction */
		si_value:  sigval,     /* [PSX] signal value */
		si_band:   c.long,     /* [PSX] band event for SIGPOLL */
		__pad:     [7]c.ulong,
	}

	ILL_ILLOPC :: 1
	ILL_ILLOPN :: 4
	ILL_ILLADR :: 5
	ILL_ILLTRP :: 2
	ILL_PRVOPC :: 3
	ILL_PRVREG :: 6
	ILL_COPROC :: 7
	ILL_BADSTK :: 8

	FPE_INTDIV :: 7
	FPE_INTOVF :: 8
	FPE_FLTDIV :: 1
	FPE_FLTOVF :: 2
	FPE_FLTUND :: 3
	FPE_FLTRES :: 4
	FPE_FLTINV :: 5
	FPE_FLTSUB :: 6

	SEGV_MAPERR :: 1
	SEGV_ACCERR :: 2

	BUS_ADRALN :: 1
	BUS_ADRERR :: 2
	BUS_OBJERR :: 3

	TRAP_BRKPT :: 1
	TRAP_TRACE :: 2

	CLD_EXITED    :: 1
	CLD_KILLED    :: 2
	CLD_DUMPED    :: 3
	CLD_TRAPPED   :: 4
	CLD_STOPPED   :: 5
	CLD_CONTINUED :: 6

	POLL_IN  :: 1
	POLL_OUT :: 2
	POLL_MSG :: 3
	POLL_ERR :: 4
	POLL_PRI :: 5
	POLL_HUP :: 6

	SI_USER    :: 0x10001
	SI_QUEUE   :: 0x10002
	SI_TIMER   :: 0x10003
	SI_ASYNCIO :: 0x10004
	SI_MESGQ   :: 0x10005

} else when ODIN_OS == .FreeBSD {

	// Request that signal be held
	SIG_HOLD :: rawptr(uintptr(3))

	uid_t :: distinct c.uint32_t

	sigset_t :: struct {
		__bits: [4]c.uint32_t,
	}

	SIGHUP    :: 1
	SIGQUIT   :: 3
	SIGTRAP   :: 5
	SIGPOLL   :: 7
	SIGKILL   :: 9
	SIGBUS    :: 10
	SIGSYS    :: 12
	SIGPIPE   :: 13
	SIGALRM   :: 14
	SIGURG    :: 16
	SIGCONT   :: 19
	SIGSTOP   :: 17
	SIGTSTP   :: 18
	SIGCHLD   :: 20
	SIGTTIN   :: 21
	SIGTTOU   :: 22
	SIGXCPU   :: 24
	SIGXFSZ   :: 25
	SIGVTALRM :: 26
	SIGPROF   :: 27
	SIGWINCH  :: 28
	SIGUSR1   :: 30
	SIGUSR2   :: 31

	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
	// `_t` has been added.

	sigaction_t :: struct {
		using _: struct #raw_union {
			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
		},
		sa_flags: SA_Flags, /* [PSX] special flags */
		sa_mask:  sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
	}

	SIG_BLOCK   :: 1
	SIG_UNBLOCK :: 2
	SIG_SETMASK :: 3

	SA_NOCLDSTOP :: 0x0008
	SA_ONSTACK   :: 0x0001
	SA_RESETHAND :: 0x0004
	SA_RESTART   :: 0x0002
	SA_SIGINFO   :: 0x0040
	SA_NOCLDWAIT :: 0x0020
	SA_NODEFER   :: 0x0010

	SS_ONSTACK :: 0x0001
	SS_DISABLE :: 0x0004

	when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
		MINSIGSTKSZ :: 1024 * 4
	} else when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
		MINSIGSTKSZ :: 512 * 4
	}

	SIGSTKSZ :: MINSIGSTKSZ + 32768

	stack_t :: struct {
		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
		ss_size:  c.size_t, /* [PSX] stack size */
		ss_flags: SS_Flags, /* [PSX] flags */
	}

	siginfo_t :: struct {
		si_signo:  Signal, /* [PSX] signal number */
		si_errno:  Errno,  /* [PSX] errno value associated with this signal */
		si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
			ill:  ILL_Code,
			fpe:  FPE_Code,
			segv: SEGV_Code,
			bus:  BUS_Code,
			trap: TRAP_Code,
			chld: CLD_Code,
			poll: POLL_Code,
			any:  Any_Code,
		},
		si_pid:    pid_t,      /* [PSX] sending process ID */
		si_uid:    uid_t,      /* [PSX] real user ID of sending process */
		si_status: c.int,      /* [PSX] exit value or signal */
		si_addr:   rawptr,     /* [PSX] address of faulting instruction */
		si_value:  sigval,     /* [PSX] signal value */
		using _reason: struct #raw_union {
			_fault: struct {
				_trapno: c.int, /* machine specific trap code */
			},
			_timer: struct {
				_timerid: c.int,
				_overrun: c.int,
			},
			_mesgq: struct {
				_mqd: c.int,
			},
			using _poll: struct {
				si_band: c.long, /* [PSX] band event for SIGPOLL */
			},
			_capsicum: struct {
				_syscall: c.int, /* syscall number for signals delivered as a result of system calls denied by capsicum */
			},
			__spare__: struct {
				__spare1__: c.long,
				__spare2__: [7]c.int,
			},
		},
	}

	ILL_ILLOPC :: 1
	ILL_ILLOPN :: 2
	ILL_ILLADR :: 3
	ILL_ILLTRP :: 4
	ILL_PRVOPC :: 5
	ILL_PRVREG :: 6
	ILL_COPROC :: 7
	ILL_BADSTK :: 8

	FPE_INTDIV :: 2
	FPE_INTOVF :: 1
	FPE_FLTDIV :: 3
	FPE_FLTOVF :: 4
	FPE_FLTUND :: 5
	FPE_FLTRES :: 6
	FPE_FLTINV :: 7
	FPE_FLTSUB :: 8

	SEGV_MAPERR :: 1
	SEGV_ACCERR :: 2

	BUS_ADRALN :: 1
	BUS_ADRERR :: 2
	BUS_OBJERR :: 3

	TRAP_BRKPT :: 1
	TRAP_TRACE :: 2

	CLD_EXITED    :: 1
	CLD_KILLED    :: 2
	CLD_DUMPED    :: 3
	CLD_TRAPPED   :: 4
	CLD_STOPPED   :: 5
	CLD_CONTINUED :: 6

	POLL_IN  :: 1
	POLL_OUT :: 2
	POLL_MSG :: 3
	POLL_ERR :: 4
	POLL_PRI :: 5
	POLL_HUP :: 6

	SI_USER    :: 0x10001
	SI_QUEUE   :: 0x10002
	SI_TIMER   :: 0x10003
	SI_ASYNCIO :: 0x10004
	SI_MESGQ   :: 0x10005

} else when ODIN_OS == .NetBSD {

	// Request that signal be held
	SIG_HOLD :: rawptr(uintptr(3))

	uid_t :: distinct c.uint32_t
	sigset_t :: struct {
		__bits: [4]c.uint32_t,
	}

	SIGHUP    :: 1
	SIGQUIT   :: 3
	SIGTRAP   :: 5
	SIGPOLL   :: 7
	SIGKILL   :: 9
	SIGBUS    :: 10
	SIGSYS    :: 12
	SIGPIPE   :: 13
	SIGALRM   :: 14
	SIGURG    :: 16
	SIGCONT   :: 19
	SIGSTOP   :: 17
	SIGTSTP   :: 18
	SIGCHLD   :: 20
	SIGTTIN   :: 21
	SIGTTOU   :: 22
	SIGXCPU   :: 24
	SIGXFSZ   :: 25
	SIGVTALRM :: 26
	SIGPROF   :: 27
	SIGWINCH  :: 28
	SIGUSR1   :: 30
	SIGUSR2   :: 31

	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
	// `_t` has been added.

	sigaction_t :: struct {
		using _: struct #raw_union {
			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
		},
		sa_mask:  sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
		sa_flags: SA_Flags, /* [PSX] special flags */
	}

	SIG_BLOCK   :: 1
	SIG_UNBLOCK :: 2
	SIG_SETMASK :: 3

	SA_NOCLDSTOP :: 0x0008
	SA_ONSTACK   :: 0x0001
	SA_RESETHAND :: 0x0004
	SA_RESTART   :: 0x0002
	SA_SIGINFO   :: 0x0040
	SA_NOCLDWAIT :: 0x0020
	SA_NODEFER   :: 0x0010

	SS_ONSTACK :: 0x0001
	SS_DISABLE :: 0x0004

	MINSIGSTKSZ :: 8192
	SIGSTKSZ    :: MINSIGSTKSZ + 32768

	stack_t :: struct {
		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
		ss_size:  c.size_t, /* [PSX] stack size */
		ss_flags: SS_Flags, /* [PSX] flags */
	}

	@(private)
	lwpid_t :: c.int32_t

	siginfo_t :: struct #raw_union {
		si_pad: [128]byte,
		using _info: struct {
			si_signo: Signal, /* [PSX] signal number */
			si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
				ill:  ILL_Code,
				fpe:  FPE_Code,
				segv: SEGV_Code,
				bus:  BUS_Code,
				trap: TRAP_Code,
				chld: CLD_Code,
				poll: POLL_Code,
				any:  Any_Code,
			},
			si_errno: Errno,  /* [PSX] errno value associated with this signal */
			// #ifdef _LP64
			/* In _LP64 the union starts on an 8-byte boundary. */
			_pad: c.int,
			// #endif
			using _reason: struct #raw_union {
				using _rt: struct {
					_pid:     pid_t,
					_uid:     uid_t,
					si_value: sigval,   /* [PSX] signal value */
				},
				using _child: struct {
					si_pid:    pid_t,   /* [PSX] sending process ID */
					si_uid:    uid_t,   /* [PSX] real user ID of sending process */
					si_status: c.int,   /* [PSX] exit value or signal */
					_utime:    clock_t,
					_stime:    clock_t,
				},
				using _fault: struct {
					si_addr: rawptr, /* [PSX] address of faulting instruction */
					_trap:   c.int,
					_trap2:  c.int,
					_trap3:  c.int,
				},
				using _poll: struct {
					si_band: c.long, /* [PSX] band event for SIGPOLL */
					_fd:     FD,
				},
				_syscall: struct {
					_sysnum:  c.int,
					_retval: [2]c.int,
					_error:   c.int,
					_args:   [8]c.uint64_t,
				},
				_ptrace_state: struct {
					_pe_report_event: c.int,
					_option: struct #raw_union {
						_pe_other_pid: pid_t,
						_pe_lwp:       lwpid_t,
					},
				},
			},
		},
	}

	ILL_ILLOPC :: 1
	ILL_ILLOPN :: 2
	ILL_ILLADR :: 3
	ILL_ILLTRP :: 4
	ILL_PRVOPC :: 5
	ILL_PRVREG :: 6
	ILL_COPROC :: 7
	ILL_BADSTK :: 8

	FPE_INTDIV :: 1
	FPE_INTOVF :: 2
	FPE_FLTDIV :: 3
	FPE_FLTOVF :: 4
	FPE_FLTUND :: 5
	FPE_FLTRES :: 6
	FPE_FLTINV :: 7
	FPE_FLTSUB :: 8

	SEGV_MAPERR :: 1
	SEGV_ACCERR :: 2

	BUS_ADRALN :: 1
	BUS_ADRERR :: 2
	BUS_OBJERR :: 3

	TRAP_BRKPT :: 1
	TRAP_TRACE :: 2

	CLD_EXITED    :: 1
	CLD_KILLED    :: 2
	CLD_DUMPED    :: 3
	CLD_TRAPPED   :: 4
	CLD_STOPPED   :: 5
	CLD_CONTINUED :: 6

	POLL_IN  :: 1
	POLL_OUT :: 2
	POLL_MSG :: 3
	POLL_ERR :: 4
	POLL_PRI :: 5
	POLL_HUP :: 6

	SI_USER    ::  0
	SI_QUEUE   :: -1
	SI_TIMER   :: -2
	SI_ASYNCIO :: -3
	SI_MESGQ   :: -4

} else when ODIN_OS == .OpenBSD {

	// Request that signal be held
	SIG_HOLD :: rawptr(uintptr(3))

	uid_t :: distinct c.uint32_t
	sigset_t :: distinct c.uint32_t

	SIGHUP    :: 1
	SIGQUIT   :: 3
	SIGTRAP   :: 5
	SIGPOLL   :: 7
	SIGKILL   :: 9
	SIGBUS    :: 10
	SIGSYS    :: 12
	SIGPIPE   :: 13
	SIGALRM   :: 14
	SIGURG    :: 16
	SIGCONT   :: 19
	SIGSTOP   :: 17
	SIGTSTP   :: 18
	SIGCHLD   :: 20
	SIGTTIN   :: 21
	SIGTTOU   :: 22
	SIGXCPU   :: 24
	SIGXFSZ   :: 25
	SIGVTALRM :: 26
	SIGPROF   :: 27
	SIGWINCH  :: 28
	SIGUSR1   :: 30
	SIGUSR2   :: 31

	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
	// `_t` has been added.

	sigaction_t :: struct {
		using _: struct #raw_union {
			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
		},
		sa_mask:  sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
		sa_flags: SA_Flags, /* [PSX] special flags */
	}

	SIG_BLOCK   :: 1
	SIG_UNBLOCK :: 2
	SIG_SETMASK :: 3

	SA_NOCLDSTOP :: 0x0008
	SA_ONSTACK   :: 0x0001
	SA_RESETHAND :: 0x0004
	SA_RESTART   :: 0x0002
	SA_SIGINFO   :: 0x0040
	SA_NOCLDWAIT :: 0x0020
	SA_NODEFER   :: 0x0010

	SS_ONSTACK :: 0x0001
	SS_DISABLE :: 0x0004

	MINSIGSTKSZ :: 3 << 12
	SIGSTKSZ    :: MINSIGSTKSZ + (1 << 12) * 4

	stack_t :: struct {
		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
		ss_size:  c.size_t, /* [PSX] stack size */
		ss_flags: SS_Flags, /* [PSX] flags */
	}

	SI_MAXSZ :: 128
	SI_PAD   :: (SI_MAXSZ / size_of(c.int)) - 3

	siginfo_t :: struct {
		si_signo: Signal, /* [PSX] signal number */
		si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
			ill:  ILL_Code,
			fpe:  FPE_Code,
			segv: SEGV_Code,
			bus:  BUS_Code,
			trap: TRAP_Code,
			chld: CLD_Code,
			poll: POLL_Code,
			any:  Any_Code,
		},
		si_errno: Errno,  /* [PSX] errno value associated with this signal */
		using _data: struct #raw_union {
			_pad: [SI_PAD]c.int,
			using _proc: struct {
				si_pid: pid_t,   /* [PSX] sending process ID */
				si_uid: uid_t,   /* [PSX] real user ID of sending process */
				using _pdata: struct #raw_union {
					using _kill: struct {
						si_value: sigval,
					},
					using _cld: struct {
						_utime:  clock_t,
						_stime:  clock_t,
						si_status: c.int,
					},
				},
			},
			using _fault: struct {
				si_addr: rawptr,
				_trapno: c.int,
			},
			using _file: struct {
				_fd: FD,
				si_band: c.long, /* [PSX] band event for SIGPOLL */
			},
		},
	}

	ILL_ILLOPC :: 1
	ILL_ILLOPN :: 2
	ILL_ILLADR :: 3
	ILL_ILLTRP :: 4
	ILL_PRVOPC :: 5
	ILL_PRVREG :: 6
	ILL_COPROC :: 7
	ILL_BADSTK :: 8

	FPE_INTDIV :: 1
	FPE_INTOVF :: 2
	FPE_FLTDIV :: 3
	FPE_FLTOVF :: 4
	FPE_FLTUND :: 5
	FPE_FLTRES :: 6
	FPE_FLTINV :: 7
	FPE_FLTSUB :: 8

	SEGV_MAPERR :: 1
	SEGV_ACCERR :: 2

	BUS_ADRALN :: 1
	BUS_ADRERR :: 2
	BUS_OBJERR :: 3

	TRAP_BRKPT :: 1
	TRAP_TRACE :: 2

	CLD_EXITED    :: 1
	CLD_KILLED    :: 2
	CLD_DUMPED    :: 3
	CLD_TRAPPED   :: 4
	CLD_STOPPED   :: 5
	CLD_CONTINUED :: 6

	POLL_IN  :: 1
	POLL_OUT :: 2
	POLL_MSG :: 3
	POLL_ERR :: 4
	POLL_PRI :: 5
	POLL_HUP :: 6

	SI_USER    ::  0
	SI_QUEUE   :: -2
	SI_TIMER   :: -3
	SI_ASYNCIO :: -4 // NOTE: not implemented
	SI_MESGQ   :: -5 // NOTE: not implemented

} else when ODIN_OS == .Linux {

	// Request that signal be held
	SIG_HOLD :: rawptr(uintptr(2))

	uid_t :: distinct c.uint32_t
	sigset_t :: struct {
		__val: [1024/(8 * size_of(c.ulong))]c.ulong,
	}

	SIGHUP    :: 1
	SIGQUIT   :: 3
	SIGTRAP   :: 5
	SIGBUS    :: 7
	SIGKILL   :: 9
	SIGUSR1   :: 10
	SIGUSR2   :: 12
	SIGPIPE   :: 13
	SIGALRM   :: 14
	SIGCHLD   :: 17
	SIGCONT   :: 18
	SIGSTOP   :: 19
	SIGTSTP   :: 20
	SIGTTIN   :: 21
	SIGTTOU   :: 22
	SIGURG    :: 23
	SIGXCPU   :: 24
	SIGXFSZ   :: 25
	SIGVTALRM :: 26
	SIGPROF   :: 27
	SIGWINCH  :: 28
	SIGPOLL   :: 29
	SIGSYS    :: 31

	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
	// `_t` has been added.

	sigaction_t :: struct {
		using _: struct #raw_union {
			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
		},
		sa_mask:     sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
		sa_flags:    SA_Flags, /* [PSX] special flags */
		sa_restorer: proc "c" (),
	}

	SIG_BLOCK   :: 0
	SIG_UNBLOCK :: 1
	SIG_SETMASK :: 2

	SA_NOCLDSTOP :: 1
	SA_NOCLDWAIT :: 2
	SA_SIGINFO   :: 4
	SA_ONSTACK   :: 0x08000000
	SA_RESTART   :: 0x10000000
	SA_NODEFER   :: 0x40000000
	SA_RESETHAND :: 0x80000000

	SS_ONSTACK :: 1
	SS_DISABLE :: 2

	when ODIN_ARCH == .arm64 {
		MINSIGSTKSZ :: 6144
		SIGSTKSZ    :: 12288
	} else {
		MINSIGSTKSZ :: 2048
		SIGSTKSZ    :: 8192
	}

	stack_t :: struct {
		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
		ss_flags: SS_Flags, /* [PSX] flags */
		ss_size:  c.size_t, /* [PSX] stack size */
	}

	@(private)
	__SI_MAX_SIZE :: 128

	when size_of(int) == 8 {
		@(private)
		_pad0 :: struct {
			_pad0: c.int,
		}
		@(private)
		__SI_PAD_SIZE :: (__SI_MAX_SIZE / size_of(c.int)) - 4

	} else {
		@(private)
		_pad0 :: struct {}
		@(private)
		__SI_PAD_SIZE :: (__SI_MAX_SIZE / size_of(c.int)) - 3
	}

	siginfo_t :: struct #align(8) {
		si_signo:  Signal, /* [PSX] signal number */
		si_errno:  Errno,  /* [PSX] errno value associated with this signal */
		si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
			ill:  ILL_Code,
			fpe:  FPE_Code,
			segv: SEGV_Code,
			bus:  BUS_Code,
			trap: TRAP_Code,
			chld: CLD_Code,
			poll: POLL_Code,
			any:  Any_Code,
		},
		__pad0: _pad0,
		using _sifields: struct #raw_union {
			_pad: [__SI_PAD_SIZE]c.int,

			using _: struct {
				si_pid: pid_t, /* [PSX] sending process ID */
				si_uid: uid_t, /* [PSX] real user ID of sending process */
				using _: struct #raw_union {
					si_status: c.int,  /* [PSX] exit value or signal */
					si_value:  sigval, /* [PSX] signal value */
				},
			},
			using _: struct {
				si_addr: rawptr, /* [PSX] address of faulting instruction */
			},
			using _: struct {
				si_band: c.long, /* [PSX] band event for SIGPOLL */
			},
		},
	}

	ILL_ILLOPC :: 1
	ILL_ILLOPN :: 2
	ILL_ILLADR :: 3
	ILL_ILLTRP :: 4
	ILL_PRVOPC :: 5
	ILL_PRVREG :: 6
	ILL_COPROC :: 7
	ILL_BADSTK :: 8

	FPE_INTDIV :: 1
	FPE_INTOVF :: 2
	FPE_FLTDIV :: 3
	FPE_FLTOVF :: 4
	FPE_FLTUND :: 5
	FPE_FLTRES :: 6
	FPE_FLTINV :: 7
	FPE_FLTSUB :: 8

	SEGV_MAPERR :: 1
	SEGV_ACCERR :: 2

	BUS_ADRALN :: 1
	BUS_ADRERR :: 2
	BUS_OBJERR :: 3

	TRAP_BRKPT :: 1
	TRAP_TRACE :: 2

	CLD_EXITED    :: 1
	CLD_KILLED    :: 2
	CLD_DUMPED    :: 3
	CLD_TRAPPED   :: 4
	CLD_STOPPED   :: 5
	CLD_CONTINUED :: 6

	POLL_IN  :: 1
	POLL_OUT :: 2
	POLL_MSG :: 3
	POLL_ERR :: 4
	POLL_PRI :: 5
	POLL_HUP :: 6

	SI_USER    :: 0
	SI_QUEUE   :: -1
	SI_TIMER   :: -2
	SI_MESGQ   :: -3
	SI_ASYNCIO :: -4

} else when ODIN_OS == .Haiku {

	// Request that signal be held
	SIG_HOLD :: rawptr(uintptr(3))

	uid_t :: distinct c.uint32_t
	sigset_t :: distinct u64

	SIGHUP     :: 1  // hangup -- tty is gone!
	//SIGINT     :: 2  // interrupt
	SIGQUIT    :: 3  // `quit' special character typed in tty
	//SIGILL     :: 4  // illegal instruction
	SIGCHLD    :: 5  // child process exited
	//SIGABRT    :: 6  // abort() called, dont' catch
	SIGPIPE    :: 7  // write to a pipe w/no readers
	//SIGFPE     :: 8  // floating point exception
	SIGKILL    :: 9  // kill a team (not catchable)
	SIGSTOP    :: 10 // suspend a thread (not catchable)
	//SIGSEGV    :: 11 // segmentation violation (read: invalid pointer)
	SIGCONT    :: 12 // continue execution if suspended
	SIGTSTP    :: 13 // `stop' special character typed in tty
	SIGALRM    :: 14 // an alarm has gone off (see alarm())
	//SIGTERM    :: 15 // termination requested
	SIGTTIN    :: 16 // read of tty from bg process
	SIGTTOU    :: 17 // write to tty from bg process
	SIGUSR1    :: 18 // app defined signal 1
	SIGUSR2    :: 19 // app defined signal 2
	SIGWINCH   :: 20 // tty window size changed
	SIGKILLTHR :: 21 // be specific: kill just the thread, not team
	SIGTRAP    :: 22 // Trace/breakpoint trap
	SIGPOLL    :: 23 // Pollable event
	SIGPROF    :: 24 // Profiling timer expired
	SIGSYS     :: 25 // Bad system call
	SIGURG     :: 26 // High bandwidth data is available at socket
	SIGVTALRM  :: 27 // Virtual timer expired
	SIGXCPU    :: 28 // CPU time limit exceeded
	SIGXFSZ    :: 29 // File size limit exceeded
	SIGBUS     :: 30 // access to undefined portion of a memory object

	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
	// `_t` has been added.

	sigaction_t :: struct {
		using _: struct #raw_union {
			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
		},
		sa_mask:     sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
		sa_flags:    SA_Flags, /* [PSX] special flags */
		sa_userdata: rawptr,   /* will be passed to the signal handler, BeOS extension */
	}

	SIG_BLOCK   :: 1
	SIG_UNBLOCK :: 2
	SIG_SETMASK :: 3

	SA_NOCLDSTOP :: 0x01
	SA_NOCLDWAIT :: 0x02
	SA_RESETHAND :: 0x04
	SA_NODEFER   :: 0x08
	SA_RESTART   :: 0x10
	SA_ONSTACK   :: 0x20
	SA_SIGINFO   :: 0x40

	SS_ONSTACK :: 1
	SS_DISABLE :: 2

	MINSIGSTKSZ :: 8192
	SIGSTKSZ    :: 16384

	stack_t :: struct {
		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
		ss_size:  c.size_t, /* [PSX] stack size */
		ss_flags: SS_Flags, /* [PSX] flags */
	}

	siginfo_t :: struct {
		si_signo: Signal,             /* [PSX] signal number */
		si_code:  struct #raw_union { /* [PSX] specific more detailed codes per signal */
			ill:  ILL_Code,
			fpe:  FPE_Code,
			segv: SEGV_Code,
			bus:  BUS_Code,
			trap: TRAP_Code,
			chld: CLD_Code,
			poll: POLL_Code,
			any:  Any_Code,
		},
		si_errno:  Errno,  /* [PSX] errno value associated with this signal */
		si_pid:    pid_t,  /* sending process ID */
		si_uid:    uid_t,  /* real user ID of sending process */
		si_addr:   rawptr, /* address of faulting instruction */
		si_status: c.int,  /* exit value or signal */
		si_band:   c.long, /* band event for SIGPOLL */
		si_value:  sigval, /* signal value */
	}

	/* any signal */
	SI_USER       :: 0 /* signal sent by user */
	SI_QUEUE      :: 1 /* signal sent by sigqueue() */
	SI_TIMER      :: 2 /* signal sent on timer_settime() timeout */
	SI_ASYNCIO    :: 3 /* signal sent on asynchronous I/O completion */
	SI_MESGQ      :: 4 /* signal sent on arrival of message on empty message queue */
	/* SIGILL */
	ILL_ILLOPC    :: 10 /* illegal opcode */
	ILL_ILLOPN    :: 11 /* illegal operand */
	ILL_ILLADR    :: 12 /* illegal addressing mode */
	ILL_ILLTRP    :: 13 /* illegal trap */
	ILL_PRVOPC    :: 14 /* privileged opcode */
	ILL_PRVREG    :: 15 /* privileged register */
	ILL_COPROC    :: 16 /* coprocessor error */
	ILL_BADSTK    :: 17 /* internal stack error */
	/* SIGFPE */
	FPE_INTDIV    :: 20 /* integer division by zero */
	FPE_INTOVF    :: 21 /* integer overflow */
	FPE_FLTDIV    :: 22 /* floating-point division by zero */
	FPE_FLTOVF    :: 23 /* floating-point overflow */
	FPE_FLTUND    :: 24 /* floating-point underflow */
	FPE_FLTRES    :: 25 /* floating-point inexact result */
	FPE_FLTINV    :: 26 /* invalid floating-point operation */
	FPE_FLTSUB    :: 27 /* subscript out of range */
	/* SIGSEGV */
	SEGV_MAPERR   :: 30 /* address not mapped to object */
	SEGV_ACCERR   :: 31 /* invalid permissions for mapped object */
	/* SIGBUS */
	BUS_ADRALN    :: 40 /* invalid address alignment */
	BUS_ADRERR    :: 41 /* nonexistent physical address */
	BUS_OBJERR    :: 42 /* object-specific hardware error */
	/* SIGTRAP */
	TRAP_BRKPT    :: 50 /* process breakpoint */
	TRAP_TRACE    :: 51 /* process trace trap. */
	/* SIGCHLD */
	CLD_EXITED    :: 60 /* child exited */
	CLD_KILLED    :: 61 /* child terminated abnormally without core dump */
	CLD_DUMPED    :: 62 /* child terminated abnormally with core dump */
	CLD_TRAPPED   :: 63 /* traced child trapped */
	CLD_STOPPED   :: 64 /* child stopped */
	CLD_CONTINUED :: 65 /* stopped child continued */
	/* SIGPOLL */
	POLL_IN       :: 70 /* input available */
	POLL_OUT      :: 71 /* output available */
	POLL_MSG      :: 72 /* input message available */
	POLL_ERR      :: 73 /* I/O error */
	POLL_PRI      :: 74 /* high priority input available */
	POLL_HUP      :: 75 /* device disconnected */

}
