# Copyright all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <http://www.gnu.org/licenses/agpl-3.0.txt>
# Dump system-specific constant numbers this is to maintain
# PublicInbox::Syscall and any other system-specific pieces.
# However, sysconf(3) constants are stable ABI on all safe to dump.
eval 'exec perl -S $0 ${1+"$@"}' # no shebang
	if 0; # running under some shell
use v5.12;
# no autodie here since some CFarm machines don't have it :<
use File::Temp 0.19;
use POSIX qw(uname);
use Config;
print STDERR '# $machine='.(POSIX::uname())[-1]."\n";
my $cc = $ENV{CC} // $Config{cc} // 'cc';
my @cflags = split(/\s+/, $ENV{CFLAGS} // $Config{ccflags} // '-Wall');
my $str = do { local $/; <DATA> };
$str =~ s/^\s*MAYBE\s*([DX])\((\w+)\)/
#ifdef $2
	$1($2);
#endif
/sgxm;
my $tmp = File::Temp->newdir('sysdefs-list-XXXX', TMPDIR => 1);
my $f = "$tmp/sysdefs.c";
my $x = "$tmp/sysdefs";
open my $fh, '>', $f or die "open $f $!";
print $fh $str or die "print $f $!";
close $fh or die "close $f $!";
for (qw(sys/ioctl sys/filio)) {
	my $cfg_name = $_;
	my $cpp_name = uc $_;
	$cfg_name =~ tr!/!!d;
	$cpp_name =~ tr!/!_!;
	($Config{"i_$cfg_name"} // '') eq 'define' and
		push @cflags, "-DHAVE_${cpp_name}_H";
}
my @cc_cmd = ($cc, '-o', $x, $f, @cflags);
if ($^O eq 'linux') {
	if (system @cc_cmd, '-DHAVE_LINUX_BTRFS_H=1') {
		warn "W: `@cc_cmd' failed w/ linux/btrfs.h, trying w/o ...\n";
	} else {
		@cc_cmd = ();
	}
}
if (@cc_cmd) {
	system(@cc_cmd) == 0 or die "`@cc_cmd' failed \$?=$?";
}
print STDERR '# %Config',
	(map { " $_=$Config{$_}" } qw(ptrsize sizesize lseeksize)), "\n";
exit(system($x)); # exit is to ensure File::Temp::Dir->DESTROY fires
__DATA__
#ifndef _GNU_SOURCE
#  define _GNU_SOURCE
#endif
#include <assert.h>
#include <signal.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#ifdef HAVE_SYS_IOCTL_H
#	include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_FILIO_H
#	include <sys/filio.h>
#endif
#ifdef __linux__
#	include <linux/fs.h>
#	include <sys/epoll.h>
#	include <sys/inotify.h>
#	include <sys/vfs.h>
#	ifdef HAVE_LINUX_BTRFS_H
#		include <linux/btrfs.h>
#	endif
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define STRUCT_BEGIN(t) do { t x; printf("'"#t"' => '%zu bytes\n", sizeof(x))
#define STRUCT_END puts("',"); } while (0)

// prints the struct field name, @offset, and signed/unsigned bit size
#define PR_NUM(f) do { \
	x.f = ~0; \
	printf("\t.%s @%zu %c%zu\n", #f, \
		offsetof(typeof(x),f), \
		x.f > 0 ? 'u' : 's', \
		sizeof(x.f) * 8); \
} while (0)

#define PR_PTR(f) do { \
	assert(sizeof(x.f) == sizeof(void *)); \
	printf("\t.%s @%zu ptr\n", #f, offsetof(typeof(x),f)); \
} while (0)

#define PR_OFF(f) do { \
	printf("\t.%s @%zu\n", #f, offsetof(typeof(x),f)); \
} while (0)

#define D(x) printf(#x " => %ld,\n", (long)x)
#define X(x) printf(#x " => 0x%lx,\n", (unsigned long)x)

int main(void)
{
	// verify Config{(ptr|size|lseek)size} entries match:
	printf("'sizeof(ptr)' => %zu,\n", sizeof(void *));
	printf("'sizeof(size_t)' => %zu,\n", sizeof(size_t));
	printf("'sizeof(off_t)' => %zu,\n", sizeof(off_t));

#ifdef __linux__
	D(SYS_epoll_create1);
	D(SYS_epoll_ctl);
	MAYBE D(SYS_epoll_pwait);

	X(IN_CLOEXEC);
	X(IN_ACCESS);
	X(IN_ALL_EVENTS);
	X(IN_ATTRIB);
	X(IN_CLOSE);
	X(IN_CLOSE_NOWRITE);
	X(IN_CLOSE_WRITE);
	X(IN_CREATE);
	X(IN_DELETE);
	X(IN_DELETE_SELF);
	X(IN_DONT_FOLLOW);
	X(IN_EXCL_UNLINK);
	X(IN_IGNORED);
	X(IN_ISDIR);
	X(IN_MASK_ADD);
	X(IN_MODIFY);
	X(IN_MOVE);
	X(IN_MOVED_FROM);
	X(IN_MOVED_TO);
	X(IN_MOVE_SELF);
	X(IN_ONESHOT);
	X(IN_ONLYDIR);
	X(IN_OPEN);
	X(IN_Q_OVERFLOW);
	X(IN_UNMOUNT);

	D(SYS_inotify_init1);
	D(SYS_inotify_add_watch);
	D(SYS_inotify_rm_watch);

	D(SYS_prctl);
	D(SYS_fstatfs);

	MAYBE X(FS_IOC_GETFLAGS);
	MAYBE X(FS_IOC_SETFLAGS);
	MAYBE X(BTRFS_IOC_DEFRAG);

	MAYBE D(SYS_renameat2);

	STRUCT_BEGIN(struct epoll_event);
		PR_NUM(events);
		PR_NUM(data.u64);
	STRUCT_END;

	STRUCT_BEGIN(struct inotify_event);
		PR_NUM(wd);
		PR_NUM(mask);
		PR_NUM(cookie);
		PR_NUM(len);
		PR_OFF(name);
	STRUCT_END;

	STRUCT_BEGIN(struct statfs);
		PR_NUM(f_type);
	STRUCT_END;
#endif /* Linux, any other OSes with stable syscalls? */

	D(SIGWINCH);
	MAYBE X(FIONREAD);
	MAYBE D(SO_ACCEPTFILTER);
	MAYBE D(_SC_NPROCESSORS_ONLN);
	MAYBE D(_SC_AVPHYS_PAGES);
	MAYBE D(_SC_PAGE_SIZE);
	MAYBE D(_SC_PAGESIZE);

	D(SYS_sendmsg);
	D(SYS_recvmsg);
	D(SYS_writev);

	STRUCT_BEGIN(struct flock);
		PR_NUM(l_start);
		PR_NUM(l_len);
		PR_NUM(l_pid);
		PR_NUM(l_type);
		PR_NUM(l_whence);
	STRUCT_END;

	STRUCT_BEGIN(struct msghdr);
		PR_PTR(msg_name);
		PR_NUM(msg_namelen);
		PR_PTR(msg_iov);
		PR_NUM(msg_iovlen);
		PR_PTR(msg_control);
		PR_NUM(msg_controllen);
		PR_NUM(msg_flags);
	STRUCT_END;

	STRUCT_BEGIN(struct cmsghdr);
		PR_NUM(cmsg_len);
		PR_NUM(cmsg_level);
		PR_NUM(cmsg_type);
	STRUCT_END;

	{
		struct cmsghdr cmsg;
		uintptr_t cmsg_data_off;
		cmsg_data_off = (uintptr_t)CMSG_DATA(&cmsg) - (uintptr_t)&cmsg;
		D(cmsg_data_off);
	}

	return 0;
}
