// SPDX-License-Identifier: GPL-2.0-only OR MIT
/*

Copyright (C) 2023 The Falco Authors.

This file is dual licensed under either the MIT or GPL 2. See MIT.txt
or GPL2.txt for full copies of the license.

*/

#ifndef PPM_FLAG_HELPERS_H_
#define PPM_FLAG_HELPERS_H_

/* The ASSERT is defined in other files that we don't
 * want to inlcude with the modern probe. `ppm.h`
 */
#ifdef __USE_VMLINUX__
#define ASSERT(expr)
#endif

#ifdef __KERNEL__
#include <linux/mman.h>
#include <linux/futex.h>
#include <linux/ptrace.h>
#include <linux/capability.h>
#include <linux/eventpoll.h>
#include <linux/prctl.h>
#include <linux/splice.h>
#ifdef __NR_finit_module
#include <uapi/linux/module.h>
#endif
#include "ppm.h"
#ifdef __NR_memfd_create
#include <uapi/linux/memfd.h>
#endif
#ifdef __NR_io_uring_register
#include <uapi/linux/io_uring.h>
#endif
#ifdef __NR_umount2
#include <linux/fs.h>
#endif
#endif  // ifndef __KERNEL__

#ifndef __always_inline
#define __always_inline inline
#endif

// When this file is included in userspace
#if !defined(__KERNEL__) && !defined(__USE_VMLINUX__)
#include <linux/futex.h>
#include <linux/dqblk_xfs.h>
#endif

#define PPM_MS_MGC_MSK 0xffff0000
#define PPM_MS_MGC_VAL 0xC0ED0000

/* Check if the res is different from `PPM_SUCCESS` */
#define CHECK_RES(x)                 \
	if(unlikely(x != PPM_SUCCESS)) { \
		return x;                    \
	}

static __always_inline uint32_t open_flags_to_scap(uint32_t flags) {
	uint32_t res = 0;

	switch(flags & (O_RDONLY | O_WRONLY | O_RDWR)) {
	case O_WRONLY:
		res |= PPM_O_WRONLY;
		break;
	case O_RDWR:
		res |= PPM_O_RDWR;
		break;
	default:
		res |= PPM_O_RDONLY;
		break;
	}

	if(flags & O_CREAT)
		res |= PPM_O_CREAT;
#ifdef O_TMPFILE
	if(flags & O_TMPFILE)
		res |= PPM_O_TMPFILE;
#endif

	if(flags & O_APPEND)
		res |= PPM_O_APPEND;

#ifdef O_DSYNC
	if(flags & O_DSYNC)
		res |= PPM_O_DSYNC;
#endif

	if(flags & O_EXCL)
		res |= PPM_O_EXCL;

#ifdef O_NONBLOCK
	if(flags & O_NONBLOCK)
		res |= PPM_O_NONBLOCK;
#endif

#ifdef O_SYNC
	if(flags & O_SYNC)
		res |= PPM_O_SYNC;
#endif

	if(flags & O_TRUNC)
		res |= PPM_O_TRUNC;

#ifdef O_DIRECT
	if(flags & O_DIRECT)
		res |= PPM_O_DIRECT;
#endif

#ifdef O_DIRECTORY
	if(flags & O_DIRECTORY)
		res |= PPM_O_DIRECTORY;
#endif

#ifdef O_LARGEFILE
	if(flags & O_LARGEFILE)
		res |= PPM_O_LARGEFILE;
#endif

#ifdef O_CLOEXEC
	if(flags & O_CLOEXEC)
		res |= PPM_O_CLOEXEC;
#endif

	return res;
}

static __always_inline uint32_t open_modes_to_scap(unsigned long flags, unsigned long modes) {
// This file is used also in userspace so we cannot use `KERNEL_VERSION` macro without an ifdef
#ifdef __KERNEL__
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
	unsigned long flags_mask = O_CREAT | O_TMPFILE;
#else
	unsigned long flags_mask = O_CREAT;
#endif
#else
	unsigned long flags_mask = O_CREAT | O_TMPFILE;
#endif

	uint32_t res = 0;

	if((flags & flags_mask) == 0)
		return res;

	if(modes & S_IRUSR)
		res |= PPM_S_IRUSR;

	if(modes & S_IWUSR)
		res |= PPM_S_IWUSR;

	if(modes & S_IXUSR)
		res |= PPM_S_IXUSR;

	/*
	 * PPM_S_IRWXU == S_IRUSR | S_IWUSR | S_IXUSR
	 */

	if(modes & S_IRGRP)
		res |= PPM_S_IRGRP;

	if(modes & S_IWGRP)
		res |= PPM_S_IWGRP;

	if(modes & S_IXGRP)
		res |= PPM_S_IXGRP;

	/*
	 * PPM_S_IRWXG == S_IRGRP | S_IWGRP | S_IXGRP
	 */

	if(modes & S_IROTH)
		res |= PPM_S_IROTH;

	if(modes & S_IWOTH)
		res |= PPM_S_IWOTH;

	if(modes & S_IXOTH)
		res |= PPM_S_IXOTH;

	/*
	 * PPM_S_IRWXO == S_IROTH | S_IWOTH | S_IXOTH
	 */

	if(modes & S_ISUID)
		res |= PPM_S_ISUID;

	if(modes & S_ISGID)
		res |= PPM_S_ISGID;

	if(modes & S_ISVTX)
		res |= PPM_S_ISVTX;

	return res;
}

static __always_inline uint32_t openat2_resolve_to_scap(unsigned long flags) {
	uint32_t res = 0;
#ifdef RESOLVE_NO_XDEV
	if(flags & RESOLVE_NO_XDEV)
		res |= PPM_RESOLVE_NO_XDEV;
#endif

#ifdef RESOLVE_NO_MAGICLINKS
	if(flags & RESOLVE_NO_MAGICLINKS)
		res |= PPM_RESOLVE_NO_MAGICLINKS;
#endif

#ifdef RESOLVE_NO_SYMLINKS
	if(flags & RESOLVE_NO_SYMLINKS)
		res |= PPM_RESOLVE_NO_SYMLINKS;
#endif

#ifdef RESOLVE_BENEATH
	if(flags & RESOLVE_BENEATH)
		res |= PPM_RESOLVE_BENEATH;
#endif

#ifdef RESOLVE_IN_ROOT
	if(flags & RESOLVE_IN_ROOT)
		res |= PPM_RESOLVE_IN_ROOT;
#endif

#ifdef RESOLVE_CACHED
	if(flags & RESOLVE_CACHED)
		res |= PPM_RESOLVE_CACHED;
#endif
	return res;
}

static __always_inline uint32_t io_uring_setup_flags_to_scap(unsigned long flags) {
	uint32_t res = 0;

#ifdef IORING_SETUP_IOPOLL
	if(flags & IORING_SETUP_IOPOLL)
		res |= PPM_IORING_SETUP_IOPOLL;
#endif

#ifdef IORING_SETUP_SQPOLL
	if(flags & IORING_SETUP_SQPOLL)
		res |= PPM_IORING_SETUP_SQPOLL;
#endif

#ifdef IORING_SQ_NEED_WAKEUP
	if(flags & IORING_SQ_NEED_WAKEUP)
		res |= PPM_IORING_SQ_NEED_WAKEUP;
#endif

#ifdef IORING_SETUP_SQ_AFF
	if(flags & IORING_SETUP_SQ_AFF)
		res |= PPM_IORING_SETUP_SQ_AFF;
#endif

#ifdef IORING_SETUP_CQSIZE
	if(flags & IORING_SETUP_CQSIZE)
		res |= PPM_IORING_SETUP_CQSIZE;
#endif

#ifdef IORING_SETUP_CLAMP
	if(flags & IORING_SETUP_CLAMP)
		res |= PPM_IORING_SETUP_CLAMP;
#endif

#ifdef IORING_SETUP_ATTACH_WQ
	if(flags & IORING_SETUP_ATTACH_WQ)
		res |= PPM_IORING_SETUP_ATTACH_WQ;
#endif

#ifdef IORING_SETUP_R_DISABLED
	if(flags & IORING_SETUP_R_DISABLED)
		res |= PPM_IORING_SETUP_R_DISABLED;
#endif
	return res;
}

static __always_inline uint32_t io_uring_setup_feats_to_scap(unsigned long flags) {
	uint32_t res = 0;

#ifdef IORING_FEAT_SINGLE_MMAP
	if(flags & IORING_FEAT_SINGLE_MMAP)
		res |= PPM_IORING_FEAT_SINGLE_MMAP;
#endif

#ifdef IORING_FEAT_NODROP
	if(flags & IORING_FEAT_NODROP)
		res |= PPM_IORING_FEAT_NODROP;
#endif

#ifdef IORING_FEAT_SUBMIT_STABLE
	if(flags & IORING_FEAT_SUBMIT_STABLE)
		res |= PPM_IORING_FEAT_SUBMIT_STABLE;
#endif

#ifdef IORING_FEAT_RW_CUR_POS
	if(flags & IORING_FEAT_RW_CUR_POS)
		res |= PPM_IORING_FEAT_RW_CUR_POS;
#endif

#ifdef IORING_FEAT_CUR_PERSONALITY
	if(flags & IORING_FEAT_CUR_PERSONALITY)
		res |= PPM_IORING_FEAT_CUR_PERSONALITY;
#endif

#ifdef IORING_FEAT_FAST_POLL
	if(flags & IORING_FEAT_FAST_POLL)
		res |= PPM_IORING_FEAT_FAST_POLL;
#endif

#ifdef IORING_FEAT_POLL_32BITS
	if(flags & IORING_FEAT_POLL_32BITS)
		res |= PPM_IORING_FEAT_POLL_32BITS;
#endif

#ifdef IORING_FEAT_SQPOLL_NONFIXED
	if(flags & IORING_FEAT_SQPOLL_NONFIXED)
		res |= PPM_IORING_FEAT_SQPOLL_NONFIXED;
#endif

#ifdef IORING_FEAT_ENTER_EXT_ARG
	if(flags & IORING_FEAT_ENTER_EXT_ARG)
		res |= PPM_IORING_FEAT_ENTER_EXT_ARG;
#endif

#ifdef IORING_FEAT_NATIVE_WORKERS
	if(flags & IORING_FEAT_NATIVE_WORKERS)
		res |= PPM_IORING_FEAT_NATIVE_WORKERS;
#endif

#ifdef IORING_FEAT_RSRC_TAGS
	if(flags & IORING_FEAT_RSRC_TAGS)
		res |= PPM_IORING_FEAT_RSRC_TAGS;
#endif
	return res;
}

static __always_inline uint32_t io_uring_enter_flags_to_scap(unsigned long flags) {
	uint32_t res = 0;

#ifdef IORING_ENTER_GETEVENTS
	if(flags & IORING_ENTER_GETEVENTS)
		res |= PPM_IORING_ENTER_GETEVENTS;
#endif

#ifdef IORING_ENTER_SQ_WAKEUP
	if(flags & IORING_ENTER_SQ_WAKEUP)
		res |= PPM_IORING_ENTER_SQ_WAKEUP;
#endif

#ifdef IORING_ENTER_SQ_WAIT
	if(flags & IORING_ENTER_SQ_WAIT)
		res |= PPM_IORING_ENTER_SQ_WAIT;
#endif

#ifdef IORING_ENTER_EXT_ARG
	if(flags & IORING_ENTER_EXT_ARG)
		res |= PPM_IORING_ENTER_EXT_ARG;
#endif
	return res;
}

static __always_inline uint32_t io_uring_register_opcodes_to_scap(unsigned long flags) {
	/*
	 * io_uring_register opcodes are defined via enum in io_uring.h.
	 * It is userspace API (thus stable) and arch independent.
	 * Therefore we map them 1:1; if any unmapped flag arrives,
	 * we will just print its value to userspace without mapping it to a string flag.
	 * We then need to append new flags to both flags_table and ppm_events_public PPM_ flags.
	 */
	return flags;
}

/* Here we don't define new flags for `inotify` since under the hood it uses the open flags.
 *
 * `/include/uapi/linux/inotify.h` from kernel source tree.
 *
 *  #define IN_CLOEXEC O_CLOEXEC
 *  #define IN_NONBLOCK O_NONBLOCK
 */
static __always_inline uint16_t inotify_init1_flags_to_scap(int32_t flags) {
	uint16_t res = 0;

	/* We need to explicitly handle the negative case otherwise `-1` will match all `flags & ...` */
	if(flags < 0) {
		return res;
	}

#ifdef O_NONBLOCK
	if(flags & O_NONBLOCK)
		res |= PPM_O_NONBLOCK;
#endif

#ifdef O_CLOEXEC
	if(flags & O_CLOEXEC)
		res |= PPM_O_CLOEXEC;
#endif

	return res;
}

/* Here we don't define new flags for `eventfd2` since under the hood it uses the open flags.
 *
 * `/include/linux/eventd.h` from kernel source tree.
 *
 *  #define EFD_SEMAPHORE (1 << 0)  <---  we don't catch this flag right now.
 *  #define EFD_CLOEXEC O_CLOEXEC
 *  #define EFD_NONBLOCK O_NONBLOCK
 */
static __always_inline uint16_t eventfd2_flags_to_scap(int32_t flags) {
	uint16_t res = 0;

	/* We need to explicitly handle the negative case otherwise `-1` will match all `flags & ...` */
	if(flags < 0) {
		return res;
	}

#ifdef O_NONBLOCK
	if(flags & O_NONBLOCK)
		res |= PPM_O_NONBLOCK;
#endif

#ifdef O_CLOEXEC
	if(flags & O_CLOEXEC)
		res |= PPM_O_CLOEXEC;
#endif

	return res;
}

/* Here we don't define new flags for `signalfd4` since under the hood it uses the open flags.
 *
 * `/include/uapi/linux/signalfd.h` from kernel source tree.
 *
 *  #define SFD_CLOEXEC O_CLOEXEC
 *  #define SFD_NONBLOCK O_NONBLOCK
 */
static __always_inline uint16_t signalfd4_flags_to_scap(int32_t flags) {
	uint16_t res = 0;

	/* We need to explicitly handle the negative case otherwise `-1` will match all `flags & ...` */
	if(flags < 0) {
		return res;
	}

#ifdef O_NONBLOCK
	if(flags & O_NONBLOCK)
		res |= PPM_O_NONBLOCK;
#endif

#ifdef O_CLOEXEC
	if(flags & O_CLOEXEC)
		res |= PPM_O_CLOEXEC;
#endif

	return res;
}

static __always_inline uint32_t clone_flags_to_scap(int flags) {
	uint32_t res = 0;

	if(flags & CLONE_FILES)
		res |= PPM_CL_CLONE_FILES;

	if(flags & CLONE_FS)
		res |= PPM_CL_CLONE_FS;

#ifdef CLONE_IO
	if(flags & CLONE_IO)
		res |= PPM_CL_CLONE_IO;
#endif

#ifdef CLONE_NEWIPC
	if(flags & CLONE_NEWIPC)
		res |= PPM_CL_CLONE_NEWIPC;
#endif

#ifdef CLONE_NEWNET
	if(flags & CLONE_NEWNET)
		res |= PPM_CL_CLONE_NEWNET;
#endif

#ifdef CLONE_NEWNS
	if(flags & CLONE_NEWNS)
		res |= PPM_CL_CLONE_NEWNS;
#endif

#ifdef CLONE_NEWPID
	if(flags & CLONE_NEWPID)
		res |= PPM_CL_CLONE_NEWPID;
#endif

#ifdef CLONE_NEWUTS
	if(flags & CLONE_NEWUTS)
		res |= PPM_CL_CLONE_NEWUTS;
#endif

	if(flags & CLONE_PARENT_SETTID)
		res |= PPM_CL_CLONE_PARENT_SETTID;

	if(flags & CLONE_PARENT)
		res |= PPM_CL_CLONE_PARENT;

	if(flags & CLONE_PTRACE)
		res |= PPM_CL_CLONE_PTRACE;

	if(flags & CLONE_SIGHAND)
		res |= PPM_CL_CLONE_SIGHAND;

	if(flags & CLONE_SYSVSEM)
		res |= PPM_CL_CLONE_SYSVSEM;

	if(flags & CLONE_THREAD)
		res |= PPM_CL_CLONE_THREAD;

	if(flags & CLONE_UNTRACED)
		res |= PPM_CL_CLONE_UNTRACED;

	if(flags & CLONE_VM)
		res |= PPM_CL_CLONE_VM;

#ifdef CLONE_NEWUSER
	if(flags & CLONE_NEWUSER)
		res |= PPM_CL_CLONE_NEWUSER;
#endif

	if(flags & CLONE_CHILD_CLEARTID)
		res |= PPM_CL_CLONE_CHILD_CLEARTID;

	if(flags & CLONE_CHILD_SETTID)
		res |= PPM_CL_CLONE_CHILD_SETTID;

	if(flags & CLONE_SETTLS)
		res |= PPM_CL_CLONE_SETTLS;

#ifdef CLONE_STOPPED
	if(flags & CLONE_STOPPED)
		res |= PPM_CL_CLONE_STOPPED;
#endif

	if(flags & CLONE_VFORK)
		res |= PPM_CL_CLONE_VFORK;

#ifdef CLONE_NEWCGROUP
	if(flags & CLONE_NEWCGROUP)
		res |= PPM_CL_CLONE_NEWCGROUP;
#endif

	return res;
}

static __always_inline uint8_t socket_family_to_scap(uint8_t family) {
	if(family == AF_INET)
		return PPM_AF_INET;
	else if(family == AF_INET6)
		return PPM_AF_INET6;
	else if(family == AF_UNIX)
		return PPM_AF_UNIX;
#ifdef AF_NETLINK
	else if(family == AF_NETLINK)
		return PPM_AF_NETLINK;
#endif
#ifdef AF_PACKET
	else if(family == AF_PACKET)
		return PPM_AF_PACKET;
#endif
#ifdef AF_UNSPEC
	else if(family == AF_UNSPEC)
		return PPM_AF_UNSPEC;
#endif
#ifdef AF_AX25
	else if(family == AF_AX25)
		return PPM_AF_AX25;
#endif
#ifdef AF_IPX
	else if(family == AF_IPX)
		return PPM_AF_IPX;
#endif
#ifdef AF_APPLETALK
	else if(family == AF_APPLETALK)
		return PPM_AF_APPLETALK;
#endif
#ifdef AF_NETROM
	else if(family == AF_NETROM)
		return PPM_AF_NETROM;
#endif
#ifdef AF_BRIDGE
	else if(family == AF_BRIDGE)
		return PPM_AF_BRIDGE;
#endif
#ifdef AF_ATMPVC
	else if(family == AF_ATMPVC)
		return PPM_AF_ATMPVC;
#endif
#ifdef AF_X25
	else if(family == AF_X25)
		return PPM_AF_X25;
#endif
#ifdef AF_ROSE
	else if(family == AF_ROSE)
		return PPM_AF_ROSE;
#endif
#ifdef AF_DECnet
	else if(family == AF_DECnet)
		return PPM_AF_DECnet;
#endif
#ifdef AF_NETBEUI
	else if(family == AF_NETBEUI)
		return PPM_AF_NETBEUI;
#endif
#ifdef AF_SECURITY
	else if(family == AF_SECURITY)
		return PPM_AF_SECURITY;
#endif
#ifdef AF_KEY
	else if(family == AF_KEY)
		return PPM_AF_KEY;
#endif
#ifdef AF_ROUTE
	else if(family == AF_ROUTE)
		return PPM_AF_ROUTE;
#endif
#ifdef AF_ASH
	else if(family == AF_ASH)
		return PPM_AF_ASH;
#endif
#ifdef AF_ECONET
	else if(family == AF_ECONET)
		return PPM_AF_ECONET;
#endif
#ifdef AF_ATMSVC
	else if(family == AF_ATMSVC)
		return PPM_AF_ATMSVC;
#endif
#ifdef AF_RDS
	else if(family == AF_RDS)
		return PPM_AF_RDS;
#endif
#ifdef AF_SNA
	else if(family == AF_SNA)
		return PPM_AF_SNA;
#endif
#ifdef AF_IRDA
	else if(family == AF_IRDA)
		return PPM_AF_IRDA;
#endif
#ifdef AF_PPPOX
	else if(family == AF_PPPOX)
		return PPM_AF_PPPOX;
#endif
#ifdef AF_WANPIPE
	else if(family == AF_WANPIPE)
		return PPM_AF_WANPIPE;
#endif
#ifdef AF_LLC
	else if(family == AF_LLC)
		return PPM_AF_LLC;
#endif
#ifdef AF_CAN
	else if(family == AF_CAN)
		return PPM_AF_CAN;
#endif
#ifdef AF_TIPC
	else if(family == AF_TIPC)
		return PPM_AF_TIPC;
#endif
#ifdef AF_BLUETOOTH
	else if(family == AF_BLUETOOTH)
		return PPM_AF_BLUETOOTH;
#endif
#ifdef AF_IUCV
	else if(family == AF_IUCV)
		return PPM_AF_IUCV;
#endif
#ifdef AF_RXRPC
	else if(family == AF_RXRPC)
		return PPM_AF_RXRPC;
#endif
#ifdef AF_ISDN
	else if(family == AF_ISDN)
		return PPM_AF_ISDN;
#endif
#ifdef AF_PHONET
	else if(family == AF_PHONET)
		return PPM_AF_PHONET;
#endif
#ifdef AF_IEEE802154
	else if(family == AF_IEEE802154)
		return PPM_AF_IEEE802154;
#endif
#ifdef AF_CAIF
	else if(family == AF_CAIF)
		return PPM_AF_CAIF;
#endif
#ifdef AF_ALG
	else if(family == AF_ALG)
		return PPM_AF_ALG;
#endif
#ifdef AF_NFC
	else if(family == AF_NFC)
		return PPM_AF_NFC;
#endif
	else {
		ASSERT(false);
		return PPM_AF_UNSPEC;
	}
}

static __always_inline uint32_t prot_flags_to_scap(int prot) {
	uint32_t res = 0;

	if(prot & PROT_READ)
		res |= PPM_PROT_READ;

	if(prot & PROT_WRITE)
		res |= PPM_PROT_WRITE;

	if(prot & PROT_EXEC)
		res |= PPM_PROT_EXEC;

#ifdef PROT_SEM
	if(prot & PROT_SEM)
		res |= PPM_PROT_SEM;
#endif

	if(prot & PROT_GROWSDOWN)
		res |= PPM_PROT_GROWSDOWN;

	if(prot & PROT_GROWSUP)
		res |= PPM_PROT_GROWSUP;

#ifdef PROT_SAO
	if(prot & PROT_SAO)
		res |= PPM_PROT_SAO;
#endif

	return res;
}

static __always_inline uint32_t mmap_flags_to_scap(int flags) {
	uint32_t res = 0;

	if(flags & MAP_SHARED)
		res |= PPM_MAP_SHARED;

	if(flags & MAP_PRIVATE)
		res |= PPM_MAP_PRIVATE;

	if(flags & MAP_FIXED)
		res |= PPM_MAP_FIXED;

	if(flags & MAP_ANONYMOUS)
		res |= PPM_MAP_ANONYMOUS;

#ifdef MAP_32BIT
	if(flags & MAP_32BIT)
		res |= PPM_MAP_32BIT;
#endif

#ifdef MAP_RENAME
	if(flags & MAP_RENAME)
		res |= PPM_MAP_RENAME;
#endif

	if(flags & MAP_NORESERVE)
		res |= PPM_MAP_NORESERVE;

	if(flags & MAP_POPULATE)
		res |= PPM_MAP_POPULATE;

	if(flags & MAP_NONBLOCK)
		res |= PPM_MAP_NONBLOCK;

	if(flags & MAP_GROWSDOWN)
		res |= PPM_MAP_GROWSDOWN;

	if(flags & MAP_DENYWRITE)
		res |= PPM_MAP_DENYWRITE;

	if(flags & MAP_EXECUTABLE)
		res |= PPM_MAP_EXECUTABLE;

#ifdef MAP_INHERIT
	if(flags & MAP_INHERIT)
		res |= PPM_MAP_INHERIT;
#endif

	if(flags & MAP_FILE)
		res |= PPM_MAP_FILE;

	if(flags & MAP_LOCKED)
		res |= PPM_MAP_LOCKED;

	return res;
}

static __always_inline uint8_t fcntl_cmd_to_scap(unsigned long cmd) {
	switch(cmd) {
	case F_DUPFD:
		return PPM_FCNTL_F_DUPFD;
	case F_GETFD:
		return PPM_FCNTL_F_GETFD;
	case F_SETFD:
		return PPM_FCNTL_F_SETFD;
	case F_GETFL:
		return PPM_FCNTL_F_GETFL;
	case F_SETFL:
		return PPM_FCNTL_F_SETFL;
	case F_GETLK:
		return PPM_FCNTL_F_GETLK;
	case F_SETLK:
		return PPM_FCNTL_F_SETLK;
	case F_SETLKW:
		return PPM_FCNTL_F_SETLKW;
	case F_SETOWN:
		return PPM_FCNTL_F_SETOWN;
	case F_GETOWN:
		return PPM_FCNTL_F_GETOWN;
	case F_SETSIG:
		return PPM_FCNTL_F_SETSIG;
	case F_GETSIG:
		return PPM_FCNTL_F_GETSIG;
// In userspace we don't want to include these flags to avoid duplicate values.
#if !defined(CONFIG_64BIT) && (defined(__KERNEL__) || defined(__USE_VMLINUX__))
	case F_GETLK64:
		return PPM_FCNTL_F_GETLK64;
	case F_SETLK64:
		return PPM_FCNTL_F_SETLK64;
	case F_SETLKW64:
		return PPM_FCNTL_F_SETLKW64;
#endif
#ifdef F_SETOWN_EX
	case F_SETOWN_EX:
		return PPM_FCNTL_F_SETOWN_EX;
#endif
#ifdef F_GETOWN_EX
	case F_GETOWN_EX:
		return PPM_FCNTL_F_GETOWN_EX;
#endif
	case F_SETLEASE:
		return PPM_FCNTL_F_SETLEASE;
	case F_GETLEASE:
		return PPM_FCNTL_F_GETLEASE;
	case F_CANCELLK:
		return PPM_FCNTL_F_CANCELLK;
#ifdef F_DUPFD_CLOEXEC
	case F_DUPFD_CLOEXEC:
		return PPM_FCNTL_F_DUPFD_CLOEXEC;
#endif
	case F_NOTIFY:
		return PPM_FCNTL_F_NOTIFY;
#ifdef F_SETPIPE_SZ
	case F_SETPIPE_SZ:
		return PPM_FCNTL_F_SETPIPE_SZ;
#endif
#ifdef F_GETPIPE_SZ
	case F_GETPIPE_SZ:
		return PPM_FCNTL_F_GETPIPE_SZ;
#endif
#ifdef F_OFD_GETLK
	case F_OFD_GETLK:
		return PPM_FCNTL_F_OFD_GETLK;
#endif
#ifdef F_OFD_SETLK
	case F_OFD_SETLK:
		return PPM_FCNTL_F_OFD_SETLK;
#endif
#ifdef F_OFD_SETLKW
	case F_OFD_SETLKW:
		return PPM_FCNTL_F_OFD_SETLKW;
#endif
	default:
		return PPM_FCNTL_UNKNOWN;
	}
}

static __always_inline uint8_t sockopt_level_to_scap(int level) {
	switch(level) {
	case SOL_SOCKET:
		return PPM_SOCKOPT_LEVEL_SOL_SOCKET;
#ifdef SOL_TCP
	case SOL_TCP:
		return PPM_SOCKOPT_LEVEL_SOL_TCP;
#endif
	default:
		/* no ASSERT as there are legitimate other levels we don't just support yet */
		return PPM_SOCKOPT_LEVEL_UNKNOWN;
	}
}

static __always_inline uint8_t sockopt_optname_to_scap(int level, int optname) {
	if(level != SOL_SOCKET) {
		/* no ASSERT as there are legitimate other levels we don't just support yet */
		return PPM_SOCKOPT_LEVEL_UNKNOWN;
	}
	switch(optname) {
#ifdef SO_DEBUG
	case SO_DEBUG:
		return PPM_SOCKOPT_SO_DEBUG;
#endif
#ifdef SO_REUSEADDR
	case SO_REUSEADDR:
		return PPM_SOCKOPT_SO_REUSEADDR;
#endif
#ifdef SO_TYPE
	case SO_TYPE:
		return PPM_SOCKOPT_SO_TYPE;
#endif
#ifdef SO_ERROR
	case SO_ERROR:
		return PPM_SOCKOPT_SO_ERROR;
#endif
#ifdef SO_DONTROUTE
	case SO_DONTROUTE:
		return PPM_SOCKOPT_SO_DONTROUTE;
#endif
#ifdef SO_BROADCAST
	case SO_BROADCAST:
		return PPM_SOCKOPT_SO_BROADCAST;
#endif
#ifdef SO_SNDBUF
	case SO_SNDBUF:
		return PPM_SOCKOPT_SO_SNDBUF;
#endif
#ifdef SO_RCVBUF
	case SO_RCVBUF:
		return PPM_SOCKOPT_SO_RCVBUF;
#endif
#ifdef SO_SNDBUFFORCE
	case SO_SNDBUFFORCE:
		return PPM_SOCKOPT_SO_SNDBUFFORCE;
#endif
#ifdef SO_RCVBUFFORCE
	case SO_RCVBUFFORCE:
		return PPM_SOCKOPT_SO_RCVBUFFORCE;
#endif
#ifdef SO_KEEPALIVE
	case SO_KEEPALIVE:
		return PPM_SOCKOPT_SO_KEEPALIVE;
#endif
#ifdef SO_OOBINLINE
	case SO_OOBINLINE:
		return PPM_SOCKOPT_SO_OOBINLINE;
#endif
#ifdef SO_NO_CHECK
	case SO_NO_CHECK:
		return PPM_SOCKOPT_SO_NO_CHECK;
#endif
#ifdef SO_PRIORITY
	case SO_PRIORITY:
		return PPM_SOCKOPT_SO_PRIORITY;
#endif
#ifdef SO_LINGER
	case SO_LINGER:
		return PPM_SOCKOPT_SO_LINGER;
#endif
#ifdef SO_BSDCOMPAT
	case SO_BSDCOMPAT:
		return PPM_SOCKOPT_SO_BSDCOMPAT;
#endif
#ifdef SO_REUSEPORT
	case SO_REUSEPORT:
		return PPM_SOCKOPT_SO_REUSEPORT;
#endif
#ifdef SO_PASSCRED
	case SO_PASSCRED:
		return PPM_SOCKOPT_SO_PASSCRED;
#endif
#ifdef SO_PEERCRED
	case SO_PEERCRED:
		return PPM_SOCKOPT_SO_PEERCRED;
#endif
#ifdef SO_RCVLOWAT
	case SO_RCVLOWAT:
		return PPM_SOCKOPT_SO_RCVLOWAT;
#endif
#ifdef SO_SNDLOWAT
	case SO_SNDLOWAT:
		return PPM_SOCKOPT_SO_SNDLOWAT;
#endif
/* We use this workaround to avoid 2 switch cases with the same value.
 * An `elif` approach is not enough if `SO_RCVTIMEO` is not defined.
 * In this case we have only `SO_RCVTIMEO_OLD` and `SO_RCVTIMEO_NEW` so
 * we couldn't be able to detect the right flag value, for example:
 * `SO_RCVTIMEO_OLD` is defined so we compile only this branch, but
 * actual value of `SO_RCVTIMEO` is `SO_RCVTIMEO_NEW`.
 * https://github.com/torvalds/linux/commit/a9beb86ae6e55bd92f38453c8623de60b8e5a308
 */
#ifdef SO_RCVTIMEO
	case SO_RCVTIMEO:
		return PPM_SOCKOPT_SO_RCVTIMEO;
#endif
#if(defined(SO_RCVTIMEO_OLD) && !defined(SO_RCVTIMEO)) || \
        (defined(SO_RCVTIMEO_OLD) && (SO_RCVTIMEO_OLD != SO_RCVTIMEO))
	case SO_RCVTIMEO_OLD:
		return PPM_SOCKOPT_SO_RCVTIMEO;
#endif
#if(defined(SO_RCVTIMEO_NEW) && !defined(SO_RCVTIMEO)) || \
        (defined(SO_RCVTIMEO_NEW) && (SO_RCVTIMEO_NEW != SO_RCVTIMEO))
	case SO_RCVTIMEO_NEW:
		return PPM_SOCKOPT_SO_RCVTIMEO;
#endif
/* Look at `SO_RCVTIMEO` */
#ifdef SO_SNDTIMEO
	case SO_SNDTIMEO:
		return PPM_SOCKOPT_SO_SNDTIMEO;
#endif
#if(defined(SO_SNDTIMEO_OLD) && !defined(SO_SNDTIMEO)) || \
        (defined(SO_SNDTIMEO_OLD) && (SO_SNDTIMEO_OLD != SO_SNDTIMEO))
	case SO_SNDTIMEO_OLD:
		return PPM_SOCKOPT_SO_SNDTIMEO;
#endif
#if(defined(SO_SNDTIMEO_NEW) && !defined(SO_SNDTIMEO)) || \
        (defined(SO_SNDTIMEO_NEW) && (SO_SNDTIMEO_NEW != SO_SNDTIMEO))
	case SO_SNDTIMEO_NEW:
		return PPM_SOCKOPT_SO_SNDTIMEO;
#endif
#ifdef SO_SECURITY_AUTHENTICATION
	case SO_SECURITY_AUTHENTICATION:
		return PPM_SOCKOPT_SO_SECURITY_AUTHENTICATION;
#endif
#ifdef SO_SECURITY_ENCRYPTION_TRANSPORT
	case SO_SECURITY_ENCRYPTION_TRANSPORT:
		return PPM_SOCKOPT_SO_SECURITY_ENCRYPTION_TRANSPORT;
#endif
#ifdef SO_SECURITY_ENCRYPTION_NETWORK
	case SO_SECURITY_ENCRYPTION_NETWORK:
		return PPM_SOCKOPT_SO_SECURITY_ENCRYPTION_NETWORK;
#endif
#ifdef SO_BINDTODEVICE
	case SO_BINDTODEVICE:
		return PPM_SOCKOPT_SO_BINDTODEVICE;
#endif
#ifdef SO_ATTACH_FILTER
	case SO_ATTACH_FILTER:
		return PPM_SOCKOPT_SO_ATTACH_FILTER;
#endif
#ifdef SO_DETACH_FILTER
	case SO_DETACH_FILTER:
		return PPM_SOCKOPT_SO_DETACH_FILTER;
#endif
#ifdef SO_PEERNAME
	case SO_PEERNAME:
		return PPM_SOCKOPT_SO_PEERNAME;
#endif
#ifdef SO_TIMESTAMP
	case SO_TIMESTAMP:
		return PPM_SOCKOPT_SO_TIMESTAMP;
#endif
#ifdef SO_ACCEPTCONN
	case SO_ACCEPTCONN:
		return PPM_SOCKOPT_SO_ACCEPTCONN;
#endif
#ifdef SO_PEERSEC
	case SO_PEERSEC:
		return PPM_SOCKOPT_SO_PEERSEC;
#endif
#ifdef SO_PASSSEC
	case SO_PASSSEC:
		return PPM_SOCKOPT_SO_PASSSEC;
#endif
#ifdef SO_TIMESTAMPNS
	case SO_TIMESTAMPNS:
		return PPM_SOCKOPT_SO_TIMESTAMPNS;
#endif
#ifdef SO_MARK
	case SO_MARK:
		return PPM_SOCKOPT_SO_MARK;
#endif
#ifdef SO_TIMESTAMPING
	case SO_TIMESTAMPING:
		return PPM_SOCKOPT_SO_TIMESTAMPING;
#endif
#ifdef SO_PROTOCOL
	case SO_PROTOCOL:
		return PPM_SOCKOPT_SO_PROTOCOL;
#endif
#ifdef SO_DOMAIN
	case SO_DOMAIN:
		return PPM_SOCKOPT_SO_DOMAIN;
#endif
#ifdef SO_RXQ_OVFL
	case SO_RXQ_OVFL:
		return PPM_SOCKOPT_SO_RXQ_OVFL;
#endif
#ifdef SO_WIFI_STATUS
	case SO_WIFI_STATUS:
		return PPM_SOCKOPT_SO_WIFI_STATUS;
#endif
#ifdef SO_PEEK_OFF
	case SO_PEEK_OFF:
		return PPM_SOCKOPT_SO_PEEK_OFF;
#endif
#ifdef SO_NOFCS
	case SO_NOFCS:
		return PPM_SOCKOPT_SO_NOFCS;
#endif
#ifdef SO_LOCK_FILTER
	case SO_LOCK_FILTER:
		return PPM_SOCKOPT_SO_LOCK_FILTER;
#endif
#ifdef SO_SELECT_ERR_QUEUE
	case SO_SELECT_ERR_QUEUE:
		return PPM_SOCKOPT_SO_SELECT_ERR_QUEUE;
#endif
#ifdef SO_BUSY_POLL
	case SO_BUSY_POLL:
		return PPM_SOCKOPT_SO_BUSY_POLL;
#endif
#ifdef SO_MAX_PACING_RATE
	case SO_MAX_PACING_RATE:
		return PPM_SOCKOPT_SO_MAX_PACING_RATE;
#endif
#ifdef SO_BPF_EXTENSIONS
	case SO_BPF_EXTENSIONS:
		return PPM_SOCKOPT_SO_BPF_EXTENSIONS;
#endif
#ifdef SO_INCOMING_CPU
	case SO_INCOMING_CPU:
		return PPM_SOCKOPT_SO_INCOMING_CPU;
#endif
#ifdef SO_ATTACH_BPF
	case SO_ATTACH_BPF:
		return PPM_SOCKOPT_SO_ATTACH_BPF;
#endif
#ifdef SO_PEERGROUPS
	case SO_PEERGROUPS:
		return PPM_SOCKOPT_SO_PEERGROUPS;
#endif
#ifdef SO_MEMINFO
	case SO_MEMINFO:
		return PPM_SOCKOPT_SO_MEMINFO;
#endif
#ifdef SO_COOKIE
	case SO_COOKIE:
		return PPM_SOCKOPT_SO_COOKIE;
#endif
#ifdef __BPF_TRACING__
	case INT_MAX:
		// forcefully disable switch jump table (clang-5 bug?)
		// Basically, when labels values are similar AND the switch has many labels,
		// compiler tends to build a jump table as optimization.
		// This breaks with eBPF, and in our Makefile we already have the -fno-jump-tables;
		// most probably clang5 had some kind of bug that caused -O2 mode to still use jump tables.
		// Let's add a "very distant" label value to forcefully disable jump table.
		//
		// DO NOT merge with below default case
		// otherwise this label will be skipped by compiler.
		ASSERT(false);
		return PPM_SOCKOPT_UNKNOWN;
#endif
	default:
		ASSERT(false);
		return PPM_SOCKOPT_UNKNOWN;
	}
}

/* XXX this is very basic for the moment, we'll need to improve it */
static __always_inline uint16_t poll_events_to_scap(short revents) {
	uint16_t res = 0;

	if(revents & POLLIN)
		res |= PPM_POLLIN;

	if(revents & POLLPRI)
		res |= PPM_POLLPRI;

	if(revents & POLLOUT)
		res |= PPM_POLLOUT;

	if(revents & POLLRDHUP)
		res |= PPM_POLLRDHUP;

	if(revents & POLLERR)
		res |= PPM_POLLERR;

	if(revents & POLLHUP)
		res |= PPM_POLLHUP;

	if(revents & POLLNVAL)
		res |= PPM_POLLNVAL;

	if(revents & POLLRDNORM)
		res |= PPM_POLLRDNORM;

	if(revents & POLLRDBAND)
		res |= PPM_POLLRDBAND;

	if(revents & POLLWRNORM)
		res |= PPM_POLLWRNORM;

	if(revents & POLLWRBAND)
		res |= PPM_POLLWRBAND;

	return res;
}

static __always_inline uint16_t futex_op_to_scap(unsigned long op) {
	uint16_t res = 0;
	unsigned long flt_op = op & 127;

	if(flt_op == FUTEX_WAIT)
		res = PPM_FU_FUTEX_WAIT;
	else if(flt_op == FUTEX_WAKE)
		res = PPM_FU_FUTEX_WAKE;
	else if(flt_op == FUTEX_FD)
		res = PPM_FU_FUTEX_FD;
	else if(flt_op == FUTEX_REQUEUE)
		res = PPM_FU_FUTEX_REQUEUE;
	else if(flt_op == FUTEX_CMP_REQUEUE)
		res = PPM_FU_FUTEX_CMP_REQUEUE;
	else if(flt_op == FUTEX_WAKE_OP)
		res = PPM_FU_FUTEX_WAKE_OP;
	else if(flt_op == FUTEX_LOCK_PI)
		res = PPM_FU_FUTEX_LOCK_PI;
	else if(flt_op == FUTEX_UNLOCK_PI)
		res = PPM_FU_FUTEX_UNLOCK_PI;
	else if(flt_op == FUTEX_TRYLOCK_PI)
		res = PPM_FU_FUTEX_TRYLOCK_PI;
#ifdef FUTEX_WAIT_BITSET
	else if(flt_op == FUTEX_WAIT_BITSET)
		res = PPM_FU_FUTEX_WAIT_BITSET;
#endif
#ifdef FUTEX_WAKE_BITSET
	else if(flt_op == FUTEX_WAKE_BITSET)
		res = PPM_FU_FUTEX_WAKE_BITSET;
#endif
#ifdef FUTEX_WAIT_REQUEUE_PI
	else if(flt_op == FUTEX_WAIT_REQUEUE_PI)
		res = PPM_FU_FUTEX_WAIT_REQUEUE_PI;
#endif
#ifdef FUTEX_CMP_REQUEUE_PI
	else if(flt_op == FUTEX_CMP_REQUEUE_PI)
		res = PPM_FU_FUTEX_CMP_REQUEUE_PI;
#endif

	if(op & FUTEX_PRIVATE_FLAG)
		res |= PPM_FU_FUTEX_PRIVATE_FLAG;

#ifdef FUTEX_CLOCK_REALTIME
	if(op & FUTEX_CLOCK_REALTIME)
		res |= PPM_FU_FUTEX_CLOCK_REALTIME;
#endif
	return res;
}

static __always_inline uint32_t access_flags_to_scap(unsigned flags) {
	uint32_t res = 0;

	if(flags == 0 /*F_OK*/) {
		res = PPM_F_OK;
	} else {
#if defined(__KERNEL__) || defined(__USE_VMLINUX__)
		if(flags & MAY_EXEC)
			res |= PPM_X_OK;
		if(flags & MAY_READ)
			res |= PPM_R_OK;
		if(flags & MAY_WRITE)
			res |= PPM_W_OK;
#else  // in userspace
		if(flags & X_OK)
			res |= PPM_X_OK;
		if(flags & R_OK)
			res |= PPM_R_OK;
		if(flags & W_OK)
			res |= PPM_W_OK;
#endif
	}

	return res;
}

static __always_inline u8 rlimit_resource_to_scap(uint32_t resource) {
	switch(resource) {
	case RLIMIT_CPU:
		return PPM_RLIMIT_CPU;
	case RLIMIT_FSIZE:
		return PPM_RLIMIT_FSIZE;
	case RLIMIT_DATA:
		return PPM_RLIMIT_DATA;
	case RLIMIT_STACK:
		return PPM_RLIMIT_STACK;
	case RLIMIT_CORE:
		return PPM_RLIMIT_CORE;
	case RLIMIT_RSS:
		return PPM_RLIMIT_RSS;
	case RLIMIT_NPROC:
		return PPM_RLIMIT_NPROC;
	case RLIMIT_NOFILE:
		return PPM_RLIMIT_NOFILE;
	case RLIMIT_MEMLOCK:
		return PPM_RLIMIT_MEMLOCK;
	case RLIMIT_AS:
		return PPM_RLIMIT_AS;
	case RLIMIT_LOCKS:
		return PPM_RLIMIT_LOCKS;
	case RLIMIT_SIGPENDING:
		return PPM_RLIMIT_SIGPENDING;
	case RLIMIT_MSGQUEUE:
		return PPM_RLIMIT_MSGQUEUE;
	case RLIMIT_NICE:
		return PPM_RLIMIT_NICE;
	case RLIMIT_RTPRIO:
		return PPM_RLIMIT_RTPRIO;
#ifdef RLIMIT_RTTIME
	case RLIMIT_RTTIME:
		return PPM_RLIMIT_RTTIME;
#endif
	default:
		return PPM_RLIMIT_UNKNOWN;
	}
}

static __always_inline uint16_t shutdown_how_to_scap(unsigned long how) {
	if(how == SHUT_RD)
		return PPM_SHUT_RD;
	else if(how == SHUT_WR)
		return PPM_SHUT_WR;
	else if(how == SHUT_RDWR)
		return PPM_SHUT_RDWR;
	return PPM_SHUT_UNKNOWN;
}

static __always_inline uint64_t lseek_whence_to_scap(unsigned long whence) {
	uint64_t res = 0;

	if(whence == SEEK_SET)
		res = PPM_SEEK_SET;
	else if(whence == SEEK_CUR)
		res = PPM_SEEK_CUR;
	else if(whence == SEEK_END)
		res = PPM_SEEK_END;

	return res;
}

static __always_inline uint16_t semop_flags_to_scap(short flags) {
	uint16_t res = 0;

	if(flags & IPC_NOWAIT)
		res |= PPM_IPC_NOWAIT;

	if(flags & SEM_UNDO)
		res |= PPM_SEM_UNDO;

	return res;
}

static __always_inline uint32_t pf_flags_to_scap(unsigned long flags) {
	uint32_t res = 0;

	/* Page fault error codes don't seem to be clearly defined in header
	 * files throughout the kernel except in some emulation modes (e.g. kvm)
	 * which we can't assume to exist, so I just took the definitions from
	 * the x86 manual. If we end up supporting another arch for page faults,
	 * refactor this.
	 */
	if(flags & 0x1)
		res |= PPM_PF_PROTECTION_VIOLATION;
	else
		res |= PPM_PF_PAGE_NOT_PRESENT;

	if(flags & 0x2)
		res |= PPM_PF_WRITE_ACCESS;
	else
		res |= PPM_PF_READ_ACCESS;

	if(flags & 0x4)
		res |= PPM_PF_USER_FAULT;
	else
		res |= PPM_PF_SUPERVISOR_FAULT;

	if(flags & 0x8)
		res |= PPM_PF_RESERVED_PAGE;

	if(flags & 0x10)
		res |= PPM_PF_INSTRUCTION_FETCH;

	return res;
}

static __always_inline uint32_t flock_flags_to_scap(int flags) {
	uint32_t res = 0;

	if(flags & LOCK_EX)
		res |= PPM_LOCK_EX;

	if(flags & LOCK_SH)
		res |= PPM_LOCK_SH;

	if(flags & LOCK_UN)
		res |= PPM_LOCK_UN;

	if(flags & LOCK_NB)
		res |= PPM_LOCK_NB;

	return res;
}

static __always_inline uint8_t quotactl_type_to_scap(unsigned long cmd) {
	switch(cmd & SUBCMDMASK) {
	case USRQUOTA:
		return PPM_USRQUOTA;
	case GRPQUOTA:
		return PPM_GRPQUOTA;
	}
	return 0;
}

static __always_inline uint16_t quotactl_cmd_to_scap(unsigned long cmd) {
	uint16_t res;

	switch(cmd >> SUBCMDSHIFT) {
	case Q_SYNC:
		res = PPM_Q_SYNC;
		break;
	case Q_QUOTAON:
		res = PPM_Q_QUOTAON;
		break;
	case Q_QUOTAOFF:
		res = PPM_Q_QUOTAOFF;
		break;
	case Q_GETFMT:
		res = PPM_Q_GETFMT;
		break;
	case Q_GETINFO:
		res = PPM_Q_GETINFO;
		break;
	case Q_SETINFO:
		res = PPM_Q_SETINFO;
		break;
	case Q_GETQUOTA:
		res = PPM_Q_GETQUOTA;
		break;
	case Q_SETQUOTA:
		res = PPM_Q_SETQUOTA;
		break;
	/*
	 *  XFS specific
	 */
	case Q_XQUOTAON:
		res = PPM_Q_XQUOTAON;
		break;
	case Q_XQUOTAOFF:
		res = PPM_Q_XQUOTAOFF;
		break;
	case Q_XGETQUOTA:
		res = PPM_Q_XGETQUOTA;
		break;
	case Q_XSETQLIM:
		res = PPM_Q_XSETQLIM;
		break;
	case Q_XGETQSTAT:
		res = PPM_Q_XGETQSTAT;
		break;
	case Q_XQUOTARM:
		res = PPM_Q_XQUOTARM;
		break;
	case Q_XQUOTASYNC:
		res = PPM_Q_XQUOTASYNC;
		break;
	default:
		res = 0;
	}
	return res;
}

static __always_inline uint8_t quotactl_fmt_to_scap(unsigned long fmt) {
	switch(fmt) {
	case QFMT_VFS_OLD:
		return PPM_QFMT_VFS_OLD;
	case QFMT_VFS_V0:
		return PPM_QFMT_VFS_V0;
#ifdef QFMT_VFS_V1
	case QFMT_VFS_V1:
		return PPM_QFMT_VFS_V1;
#endif
	default:
		return PPM_QFMT_NOT_USED;
	}
}

static __always_inline uint32_t semget_flags_to_scap(unsigned flags) {
	uint32_t res = 0;

	if(flags & IPC_CREAT)
		res |= PPM_IPC_CREAT;

	if(flags & IPC_EXCL)
		res |= PPM_IPC_EXCL;

	return res;
}

static __always_inline uint32_t semctl_cmd_to_scap(unsigned cmd) {
	switch(cmd) {
	case IPC_STAT:
		return PPM_IPC_STAT;
	case IPC_SET:
		return PPM_IPC_SET;
	case IPC_RMID:
		return PPM_IPC_RMID;
	case IPC_INFO:
		return PPM_IPC_INFO;
	case SEM_INFO:
		return PPM_SEM_INFO;
	case SEM_STAT:
		return PPM_SEM_STAT;
	case GETALL:
		return PPM_GETALL;
	case GETNCNT:
		return PPM_GETNCNT;
	case GETPID:
		return PPM_GETPID;
	case GETVAL:
		return PPM_GETVAL;
	case GETZCNT:
		return PPM_GETZCNT;
	case SETALL:
		return PPM_SETALL;
	case SETVAL:
		return PPM_SETVAL;
#ifdef __BPF_TRACING__
	// forcefully disable switch jump table, see sockopt_optname_to_scap() for more info
	case INT_MAX:
		return 0;
#endif
	}
	return 0;
}

static __always_inline uint16_t ptrace_requests_to_scap(unsigned long req) {
	switch(req) {
#ifdef PTRACE_SINGLEBLOCK
	case PTRACE_SINGLEBLOCK:
		return PPM_PTRACE_SINGLEBLOCK;
#endif
#ifdef PTRACE_SYSEMU_SINGLESTEP
	case PTRACE_SYSEMU_SINGLESTEP:
		return PPM_PTRACE_SYSEMU_SINGLESTEP;
#endif

#ifdef PTRACE_SYSEMU
	case PTRACE_SYSEMU:
		return PPM_PTRACE_SYSEMU;
#endif
#ifdef PTRACE_ARCH_PRCTL
	case PTRACE_ARCH_PRCTL:
		return PPM_PTRACE_ARCH_PRCTL;
#endif
#ifdef PTRACE_SET_THREAD_AREA
	case PTRACE_SET_THREAD_AREA:
		return PPM_PTRACE_SET_THREAD_AREA;
#endif
#ifdef PTRACE_GET_THREAD_AREA
	case PTRACE_GET_THREAD_AREA:
		return PPM_PTRACE_GET_THREAD_AREA;
#endif
#ifdef PTRACE_OLDSETOPTIONS
	case PTRACE_OLDSETOPTIONS:
		return PPM_PTRACE_OLDSETOPTIONS;
#endif
#ifdef PTRACE_SETFPXREGS
	case PTRACE_SETFPXREGS:
		return PPM_PTRACE_SETFPXREGS;
#endif
#ifdef PTRACE_GETFPXREGS
	case PTRACE_GETFPXREGS:
		return PPM_PTRACE_GETFPXREGS;
#endif
#ifdef PTRACE_SETFPREGS
	case PTRACE_SETFPREGS:
		return PPM_PTRACE_SETFPREGS;
#endif
#ifdef PTRACE_GETFPREGS
	case PTRACE_GETFPREGS:
		return PPM_PTRACE_GETFPREGS;
#endif
#ifdef PTRACE_SETREGS
	case PTRACE_SETREGS:
		return PPM_PTRACE_SETREGS;
#endif
#ifdef PTRACE_GETREGS
	case PTRACE_GETREGS:
		return PPM_PTRACE_GETREGS;
#endif
#ifdef PTRACE_SETSIGMASK
	case PTRACE_SETSIGMASK:
		return PPM_PTRACE_SETSIGMASK;
#endif
#ifdef PTRACE_GETSIGMASK
	case PTRACE_GETSIGMASK:
		return PPM_PTRACE_GETSIGMASK;
#endif
#ifdef PTRACE_PEEKSIGINFO
	case PTRACE_PEEKSIGINFO:
		return PPM_PTRACE_PEEKSIGINFO;
#endif
#ifdef PTRACE_LISTEN
	case PTRACE_LISTEN:
		return PPM_PTRACE_LISTEN;
#endif
#ifdef PTRACE_INTERRUPT
	case PTRACE_INTERRUPT:
		return PPM_PTRACE_INTERRUPT;
#endif
#ifdef PTRACE_SEIZE
	case PTRACE_SEIZE:
		return PPM_PTRACE_SEIZE;
#endif
#ifdef PTRACE_SETREGSET
	case PTRACE_SETREGSET:
		return PPM_PTRACE_SETREGSET;
#endif
#ifdef PTRACE_GETREGSET
	case PTRACE_GETREGSET:
		return PPM_PTRACE_GETREGSET;
#endif
	case PTRACE_SETSIGINFO:
		return PPM_PTRACE_SETSIGINFO;
	case PTRACE_GETSIGINFO:
		return PPM_PTRACE_GETSIGINFO;
	case PTRACE_GETEVENTMSG:
		return PPM_PTRACE_GETEVENTMSG;
	case PTRACE_SETOPTIONS:
		return PPM_PTRACE_SETOPTIONS;
	case PTRACE_SYSCALL:
		return PPM_PTRACE_SYSCALL;
	case PTRACE_DETACH:
		return PPM_PTRACE_DETACH;
	case PTRACE_ATTACH:
		return PPM_PTRACE_ATTACH;
	case PTRACE_SINGLESTEP:
		return PPM_PTRACE_SINGLESTEP;
	case PTRACE_KILL:
		return PPM_PTRACE_KILL;
	case PTRACE_CONT:
		return PPM_PTRACE_CONT;
#ifdef PTRACE_POKEUSR
	case PTRACE_POKEUSR:
		return PPM_PTRACE_POKEUSR;
#endif
	case PTRACE_POKEDATA:
		return PPM_PTRACE_POKEDATA;
	case PTRACE_POKETEXT:
		return PPM_PTRACE_POKETEXT;
#ifdef PTRACE_PEEKUSR
	case PTRACE_PEEKUSR:
		return PPM_PTRACE_PEEKUSR;
#endif
	case PTRACE_PEEKDATA:
		return PPM_PTRACE_PEEKDATA;
	case PTRACE_PEEKTEXT:
		return PPM_PTRACE_PEEKTEXT;
	case PTRACE_TRACEME:
		return PPM_PTRACE_TRACEME;
	default:
		return PPM_PTRACE_UNKNOWN;
	}
}

static __always_inline uint32_t execveat_flags_to_scap(unsigned long flags) {
	uint32_t res = 0;

#ifdef AT_EMPTY_PATH
	if(flags & AT_EMPTY_PATH)
		res |= PPM_EXVAT_AT_EMPTY_PATH;
#endif

#ifdef AT_SYMLINK_NOFOLLOW
	if(flags & AT_SYMLINK_NOFOLLOW)
		res |= PPM_EXVAT_AT_SYMLINK_NOFOLLOW;
#endif

	return res;
}

static __always_inline uint32_t fsconfig_cmds_to_scap(uint32_t cmd) {
	/*
	 * fsconfig opcodes are defined via enum in uapi/linux/mount.h.
	 * It is userspace API (thus stable) and arch-independent.
	 * Therefore we map them 1:1; if any unmapped flag arrives,
	 * we will just print its value to userspace without mapping it to a string flag.
	 * We then need to append new flags to both flags_table and ppm_events_public PPM_ flags.
	 */
	return cmd;
}

static __always_inline uint32_t mlockall_flags_to_scap(unsigned long flags) {
	uint32_t res = 0;
#ifdef MCL_CURRENT
	if(flags & MCL_CURRENT)
		res |= PPM_MLOCKALL_MCL_CURRENT;
#endif
#ifdef MCL_FUTURE
	if(flags & MCL_FUTURE)
		res |= PPM_MLOCKALL_MCL_FUTURE;
#endif
#ifdef MCL_ONFAULT
	if(flags & MCL_ONFAULT)
		res |= PPM_MLOCKALL_MCL_ONFAULT;
#endif
	return res;
}

static __always_inline uint32_t mlock2_flags_to_scap(unsigned long flags) {
	uint32_t res = 0;
#ifdef MLOCK_ONFAULT
	if(flags & MLOCK_ONFAULT)
		res |= PPM_MLOCK_ONFAULT;
#endif
	return res;
}

static __always_inline uint32_t memfd_create_flags_to_scap(uint32_t flags) {
	uint32_t res = 0;
#ifdef MFD_CLOEXEC
	if(flags & MFD_CLOEXEC)
		res |= PPM_MFD_CLOEXEC;
#endif
#ifdef MFD_ALLOW_SEALING
	if(flags & MFD_ALLOW_SEALING)
		res |= PPM_MFD_ALLOW_SEALING;
#endif
#ifdef MFD_HUGETLB
	if(flags & MFD_HUGETLB)
		res |= PPM_MFD_HUGETLB;
#endif
	return res;
}

static __always_inline uint32_t unlinkat_flags_to_scap(int32_t flags) {
	uint32_t res = 0;

	if(flags & AT_REMOVEDIR)
		res |= PPM_AT_REMOVEDIR;

	return res;
}

static __always_inline uint32_t linkat_flags_to_scap(int32_t flags) {
	uint32_t res = 0;

	if(flags & AT_SYMLINK_FOLLOW)
		res |= PPM_AT_SYMLINK_FOLLOW;

#ifdef AT_EMPTY_PATH
	if(flags & AT_EMPTY_PATH)
		res |= PPM_AT_EMPTY_PATH;
#endif

	return res;
}

static __always_inline uint32_t newfstatat_flags_to_scap(int32_t flags) {
	uint32_t res = 0;

	if(flags & AT_SYMLINK_NOFOLLOW)
		res |= PPM_AT_SYMLINK_NOFOLLOW;

#ifdef AT_EMPTY_PATH
	if(flags & AT_EMPTY_PATH)
		res |= PPM_AT_EMPTY_PATH;
#endif

#ifdef AT_NO_AUTOMOUNT
	if(flags & AT_NO_AUTOMOUNT)
		res |= PPM_AT_NO_AUTOMOUNT;
#endif

	return res;
}

static __always_inline uint32_t chmod_mode_to_scap(unsigned long modes) {
	uint32_t res = 0;
	if(modes & S_IRUSR)
		res |= PPM_S_IRUSR;

	if(modes & S_IWUSR)
		res |= PPM_S_IWUSR;

	if(modes & S_IXUSR)
		res |= PPM_S_IXUSR;

	/*
	 * PPM_S_IRWXU == S_IRUSR | S_IWUSR | S_IXUSR
	 */

	if(modes & S_IRGRP)
		res |= PPM_S_IRGRP;

	if(modes & S_IWGRP)
		res |= PPM_S_IWGRP;

	if(modes & S_IXGRP)
		res |= PPM_S_IXGRP;

	/*
	 * PPM_S_IRWXG == S_IRGRP | S_IWGRP | S_IXGRP
	 */

	if(modes & S_IROTH)
		res |= PPM_S_IROTH;

	if(modes & S_IWOTH)
		res |= PPM_S_IWOTH;

	if(modes & S_IXOTH)
		res |= PPM_S_IXOTH;

	/*
	 * PPM_S_IRWXO == S_IROTH | S_IWOTH | S_IXOTH
	 */

	if(modes & S_ISUID)
		res |= PPM_S_ISUID;

	if(modes & S_ISGID)
		res |= PPM_S_ISGID;

	if(modes & S_ISVTX)
		res |= PPM_S_ISVTX;

	return res;
}

static __always_inline uint32_t umount2_flags_to_scap(int flags) {
	uint32_t res = 0;

#ifdef MNT_FORCE
	if(flags & MNT_FORCE)
		res |= PPM_MNT_FORCE;
#endif
#ifdef MNT_DETACH
	if(flags & MNT_DETACH)
		res |= PPM_MNT_DETACH;
#endif
#ifdef MNT_EXPIRE
	if(flags & MNT_EXPIRE)
		res |= PPM_MNT_EXPIRE;
#endif
#ifdef UMOUNT_NOFOLLOW
	if(flags & UMOUNT_NOFOLLOW)
		res |= PPM_UMOUNT_NOFOLLOW;
#endif
	return res;
}

static __always_inline uint32_t fchownat_flags_to_scap(unsigned long flags) {
	uint32_t res = 0;

#ifdef AT_SYMLINK_FOLLOW
	if(flags & AT_SYMLINK_FOLLOW)
		res |= PPM_AT_SYMLINK_FOLLOW;
#endif

#ifdef AT_EMPTY_PATH
	if(flags & AT_EMPTY_PATH)
		res |= PPM_AT_EMPTY_PATH;
#endif

	return res;
}

static __always_inline uint64_t capabilities_to_scap(unsigned long caps) {
	uint64_t res = 0;

#ifdef CAP_CHOWN
	if(caps & (1UL << CAP_CHOWN))
		res |= PPM_CAP_CHOWN;
#endif
#ifdef CAP_DAC_OVERRIDE
	if(caps & (1UL << CAP_DAC_OVERRIDE))
		res |= PPM_CAP_DAC_OVERRIDE;
#endif
#ifdef CAP_DAC_READ_SEARCH
	if(caps & (1UL << CAP_DAC_READ_SEARCH))
		res |= PPM_CAP_DAC_READ_SEARCH;
#endif
#ifdef CAP_FOWNER
	if(caps & (1UL << CAP_FOWNER))
		res |= PPM_CAP_FOWNER;
#endif
#ifdef CAP_FSETID
	if(caps & (1UL << CAP_FSETID))
		res |= PPM_CAP_FSETID;
#endif
#ifdef CAP_KILL
	if(caps & (1UL << CAP_KILL))
		res |= PPM_CAP_KILL;
#endif
#ifdef CAP_SETGID
	if(caps & (1UL << CAP_SETGID))
		res |= PPM_CAP_SETGID;
#endif
#ifdef CAP_SETUID
	if(caps & (1UL << CAP_SETUID))
		res |= PPM_CAP_SETUID;
#endif
#ifdef CAP_SETPCAP
	if(caps & (1UL << CAP_SETPCAP))
		res |= PPM_CAP_SETPCAP;
#endif
#ifdef CAP_LINUX_IMMUTABLE
	if(caps & (1UL << CAP_LINUX_IMMUTABLE))
		res |= PPM_CAP_LINUX_IMMUTABLE;
#endif
#ifdef CAP_NET_BIND_SERVICE
	if(caps & (1UL << CAP_NET_BIND_SERVICE))
		res |= PPM_CAP_NET_BIND_SERVICE;
#endif
#ifdef CAP_NET_BROADCAST
	if(caps & (1UL << CAP_NET_BROADCAST))
		res |= PPM_CAP_NET_BROADCAST;
#endif
#ifdef CAP_NET_ADMIN
	if(caps & (1UL << CAP_NET_ADMIN))
		res |= PPM_CAP_NET_ADMIN;
#endif
#ifdef CAP_NET_RAW
	if(caps & (1UL << CAP_NET_RAW))
		res |= PPM_CAP_NET_RAW;
#endif
#ifdef CAP_IPC_LOCK
	if(caps & (1UL << CAP_IPC_LOCK))
		res |= PPM_CAP_IPC_LOCK;
#endif
#ifdef CAP_IPC_OWNER
	if(caps & (1UL << CAP_IPC_OWNER))
		res |= PPM_CAP_IPC_OWNER;
#endif
#ifdef CAP_SYS_MODULE
	if(caps & (1UL << CAP_SYS_MODULE))
		res |= PPM_CAP_SYS_MODULE;
#endif
#ifdef CAP_SYS_RAWIO
	if(caps & (1UL << CAP_SYS_RAWIO))
		res |= PPM_CAP_SYS_RAWIO;
#endif
#ifdef CAP_SYS_CHROOT
	if(caps & (1UL << CAP_SYS_CHROOT))
		res |= PPM_CAP_SYS_CHROOT;
#endif
#ifdef CAP_SYS_PTRACE
	if(caps & (1UL << CAP_SYS_PTRACE))
		res |= PPM_CAP_SYS_PTRACE;
#endif
#ifdef CAP_SYS_PACCT
	if(caps & (1UL << CAP_SYS_PACCT))
		res |= PPM_CAP_SYS_PACCT;
#endif
#ifdef CAP_SYS_ADMIN
	if(caps & (1UL << CAP_SYS_ADMIN))
		res |= PPM_CAP_SYS_ADMIN;
#endif
#ifdef CAP_SYS_BOOT
	if(caps & (1UL << CAP_SYS_BOOT))
		res |= PPM_CAP_SYS_BOOT;
#endif
#ifdef CAP_SYS_NICE
	if(caps & (1UL << CAP_SYS_NICE))
		res |= PPM_CAP_SYS_NICE;
#endif
#ifdef CAP_SYS_RESOURCE
	if(caps & (1UL << CAP_SYS_RESOURCE))
		res |= PPM_CAP_SYS_RESOURCE;
#endif
#ifdef CAP_SYS_TIME
	if(caps & (1UL << CAP_SYS_TIME))
		res |= PPM_CAP_SYS_TIME;
#endif
#ifdef CAP_SYS_TTY_CONFIG
	if(caps & (1UL << CAP_SYS_TTY_CONFIG))
		res |= PPM_CAP_SYS_TTY_CONFIG;
#endif
#ifdef CAP_MKNOD
	if(caps & (1UL << CAP_MKNOD))
		res |= PPM_CAP_MKNOD;
#endif
#ifdef CAP_LEASE
	if(caps & (1UL << CAP_LEASE))
		res |= PPM_CAP_LEASE;
#endif
#ifdef CAP_AUDIT_WRITE
	if(caps & (1UL << CAP_AUDIT_WRITE))
		res |= PPM_CAP_AUDIT_WRITE;
#endif
#ifdef CAP_AUDIT_CONTROL
	if(caps & (1UL << CAP_AUDIT_CONTROL))
		res |= PPM_CAP_AUDIT_CONTROL;
#endif
#ifdef CAP_SETFCAP
	if(caps & (1UL << CAP_SETFCAP))
		res |= PPM_CAP_SETFCAP;
#endif
#ifdef CAP_MAC_OVERRIDE
	if(caps & (1UL << CAP_MAC_OVERRIDE))
		res |= PPM_CAP_MAC_OVERRIDE;
#endif
#ifdef CAP_MAC_ADMIN
	if(caps & (1UL << CAP_MAC_ADMIN))
		res |= PPM_CAP_MAC_ADMIN;
#endif
#ifdef CAP_SYSLOG
	if(caps & (1UL << CAP_SYSLOG))
		res |= PPM_CAP_SYSLOG;
#endif
#ifdef CAP_WAKE_ALARM
	if(caps & (1UL << CAP_WAKE_ALARM))
		res |= PPM_CAP_WAKE_ALARM;
#endif
#ifdef CAP_BLOCK_SUSPEND
	if(caps & (1UL << CAP_BLOCK_SUSPEND))
		res |= PPM_CAP_BLOCK_SUSPEND;
#endif
#ifdef CAP_AUDIT_READ
	if(caps & (1UL << CAP_AUDIT_READ))
		res |= PPM_CAP_AUDIT_READ;
#endif
#ifdef CAP_PERFMON
	if(caps & (1UL << CAP_PERFMON))
		res |= PPM_CAP_PERFMON;
#endif
#ifdef CAP_BPF
	if(caps & (1UL << CAP_BPF))
		res |= PPM_CAP_BPF;
#endif
#ifdef CAP_CHECKPOINT_RESTORE
	if(caps & (1UL << CAP_CHECKPOINT_RESTORE))
		res |= PPM_CAP_CHECKPOINT_RESTORE;
#endif

	return res;
}

static __always_inline uint32_t dup3_flags_to_scap(int flags) {
	uint32_t res = 0;
#ifdef O_CLOEXEC
	if(flags & O_CLOEXEC)
		res |= PPM_O_CLOEXEC;
#endif
	return res;
}

static __always_inline uint32_t pipe2_flags_to_scap(int32_t flags) {
	uint32_t res = 0;

	/* We need to explicitly handle the negative case otherwise `-1` will match all `flags & ...` */
	if(flags < 0) {
		return res;
	}

#ifdef O_CLOEXEC
	if(flags & O_CLOEXEC)
		res |= PPM_O_CLOEXEC;
#endif
#ifdef O_DIRECT
	if(flags & O_DIRECT)
		res |= PPM_O_DIRECT;
#endif
#ifdef O_NONBLOCK
	if(flags & O_NONBLOCK)
		res |= PPM_O_NONBLOCK;
#endif
	return res;
}

static __always_inline uint32_t epoll_create1_flags_to_scap(uint32_t flags) {
	uint32_t res = 0;
#ifdef EPOLL_CLOEXEC
	if(flags & EPOLL_CLOEXEC)
		res |= PPM_EPOLL_CLOEXEC;
#endif
	return res;
}

static __always_inline uint32_t splice_flags_to_scap(uint32_t flags) {
	uint32_t res = 0;
#ifdef SPLICE_F_MOVE
	if(flags & SPLICE_F_MOVE)
		res |= PPM_SPLICE_F_MOVE;
#endif
#ifdef SPLICE_F_NONBLOCK
	if(flags & SPLICE_F_NONBLOCK)
		res |= PPM_SPLICE_F_NONBLOCK;
#endif
#ifdef SPLICE_F_MORE
	if(flags & SPLICE_F_MORE)
		res |= PPM_SPLICE_F_MORE;
#endif
#ifdef SPLICE_F_GIFT
	if(flags & SPLICE_F_GIFT)
		res |= PPM_SPLICE_F_GIFT;
#endif
	return res;
}

static __always_inline uint32_t pidfd_open_flags_to_scap(uint32_t flags) {
	uint32_t res = 0;
// See https://elixir.bootlin.com/linux/v5.10.185/source/include/uapi/linux/pidfd.h#L10
#ifdef O_NONBLOCK
	if(flags & O_NONBLOCK)
		res |= PPM_PIDFD_NONBLOCK;
#endif
	return res;
}

#ifdef OVERLAYFS_SUPER_MAGIC
#define PPM_OVERLAYFS_SUPER_MAGIC OVERLAYFS_SUPER_MAGIC
#else
#define PPM_OVERLAYFS_SUPER_MAGIC 0x794c7630
#endif

static __always_inline uint32_t prctl_options_to_scap(int options) {
	return (uint32_t)options;
}

static __always_inline uint32_t finit_module_flags_to_scap(int32_t flags) {
	int32_t res = 0;
#ifdef MODULE_INIT_IGNORE_MODVERSIONS
	if(flags & MODULE_INIT_IGNORE_MODVERSIONS)
		res |= PPM_MODULE_INIT_IGNORE_MODVERSIONS;
#endif

#ifdef MODULE_INIT_IGNORE_VERMAGIC
	if(flags & MODULE_INIT_IGNORE_VERMAGIC)
		res |= PPM_MODULE_INIT_IGNORE_VERMAGIC;
#endif

#ifdef MODULE_INIT_COMPRESSED_FILE
	if(flags & MODULE_INIT_COMPRESSED_FILE)
		res |= PPM_MODULE_INIT_COMPRESSED_FILE;
#endif

	return res;
}

static __always_inline uint32_t mknod_mode_to_scap(uint32_t modes) {
	uint32_t res = chmod_mode_to_scap(modes);

	/*
	 * mknod modes
	 */

#ifdef S_IFMT
	switch(modes & S_IFMT) {
#ifdef S_IFSOCK
	case S_IFSOCK:
		res |= PPM_S_IFSOCK;
		break;
#endif
#ifdef S_IFREG
	// Zero file type is equivalent to type S_IFREG.
	case 0:
	case S_IFREG:
		res |= PPM_S_IFREG;
		break;
#endif
#ifdef S_IFBLK
	case S_IFBLK:
		res |= PPM_S_IFBLK;
		break;
#endif
#ifdef S_IFCHR
	case S_IFCHR:
		res |= PPM_S_IFCHR;
		break;
#endif
#ifdef S_IFIFO
	case S_IFIFO:
		res |= PPM_S_IFIFO;
		break;
#endif
	default:
		break;
	}
#endif

	return res;
}

static __always_inline uint32_t bpf_cmd_to_scap(unsigned long cmd) {
	/*
	 * bpf opcodes are defined via enum in uapi/linux/bpf.h.
	 * It is userspace API (thus stable) and arch-independent.
	 * Therefore we map them 1:1; if any unmapped flag arrives,
	 * we will just print its value to userspace without mapping it to a string flag.
	 * We then need to append new flags to both flags_table and ppm_events_public PPM_ flags.
	 */

	return cmd;
}

static __always_inline uint32_t delete_module_flags_to_scap(unsigned long flags) {
	uint32_t res = 0;
#ifdef O_NONBLOCK
	if(flags & O_NONBLOCK)
		res |= PPM_DELETE_MODULE_O_NONBLOCK;
#endif
#ifdef O_TRUNC
	if(flags & O_TRUNC)
		res |= PPM_DELETE_MODULE_O_TRUNC;
#endif
	return res;
}

#endif /* PPM_FLAG_HELPERS_H_ */
