#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>

#if defined MAJOR_IN_SYSMACROS
# include <sys/sysmacros.h>
#elif defined MAJOR_IN_MKDEV
# include <sys/mkdev.h>
#else
# include <sys/types.h>
#endif

#undef STAT_PREFIX
#undef NR_stat

#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64
# include <sys/stat.h>
# define STAT_PREFIX "(stat(64)?\\(|newfstatat\\(AT_FDCWD, )"
#else
# include <sys/syscall.h>
# if defined __NR_stat
#  define NR_stat __NR_stat
#  define STAT_PREFIX "stat\\("
# elif defined __NR_newstat
#  define NR_stat __NR_newstat
#  define STAT_PREFIX "newstat\\("
# endif
# ifdef STAT_PREFIX
/* for S_IFMT */
#  define stat libc_stat
#  define stat64 libc_stat64
#  include <sys/stat.h>
#  undef stat
#  undef stat64
#  undef st_atime
#  undef st_mtime
#  undef st_ctime

#  undef dev_t
#  undef ino_t
#  undef mode_t
#  undef nlink_t
#  undef uid_t
#  undef gid_t
#  undef off_t
#  undef loff_t
#  define dev_t __kernel_dev_t
#  define ino_t __kernel_ino_t
#  define mode_t __kernel_mode_t
#  define nlink_t __kernel_nlink_t
#  define uid_t __kernel_uid_t
#  define gid_t __kernel_gid_t
#  define off_t __kernel_off_t
#  define loff_t __kernel_loff_t
#  include <asm/stat.h>
#  endif /* STAT_PREFIX */
#endif /* _FILE_OFFSET_BITS */

#ifdef STAT_PREFIX

static void
print_ftype(unsigned int mode)
{
	if (S_ISREG(mode))
		printf("S_IFREG");
	else if (S_ISDIR(mode))
		printf("S_IFDIR");
	else if (S_ISCHR(mode))
		printf("S_IFCHR");
	else if (S_ISBLK(mode))
		printf("S_IFBLK");
	else
		printf("%#o", mode & S_IFMT);
}

static void
print_perms(unsigned int mode)
{
	printf("%#o", mode & ~S_IFMT);
}

static void
print_time(time_t t)
{
	if (!t) {
		printf("0");
		return;
	}

	struct tm *p = localtime(&t);

	if (p)
		printf("%02d/%02d/%02d-%02d:%02d:%02d",
		       p->tm_year + 1900, p->tm_mon + 1, p->tm_mday,
		       p->tm_hour, p->tm_min, p->tm_sec);
	else
		printf("%llu", (unsigned long long) t);
}

int
main(int ac, const char **av)
{
	assert(ac == 2);
	struct stat stb;

#ifdef NR_stat
	if (sizeof(stb.st_size) > 4)
		return 77;
	assert(syscall(NR_stat, av[1], &stb) == 0);
#else
	assert(stat(av[1], &stb) == 0);
#endif

	printf(STAT_PREFIX "\"%s\", \\{", av[1]);
	printf("st_dev=makedev\\(%u, %u\\)",
	       (unsigned int) major(stb.st_dev),
	       (unsigned int) minor(stb.st_dev));
	printf(", st_ino=%llu", (unsigned long long) stb.st_ino);
	printf(", st_mode=");
		print_ftype(stb.st_mode);
		printf("\\|");
		print_perms(stb.st_mode);
	printf(", st_nlink=%u", (unsigned int) stb.st_nlink);
	printf(", st_uid=%u", (unsigned int) stb.st_uid);
	printf(", st_gid=%u", (unsigned int) stb.st_gid);
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
	printf(", st_blksize=%u", (unsigned int) stb.st_blksize);
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
	printf(", st_blocks=%u", (unsigned int) stb.st_blocks);
#endif

	switch (stb.st_mode & S_IFMT) {
	case S_IFCHR: case S_IFBLK:
#ifdef HAVE_STRUCT_STAT_ST_RDEV
		printf(", st_rdev=makedev\\(%u, %u\\)",
		       (unsigned int) major(stb.st_rdev),
		       (unsigned int) minor(stb.st_rdev));
#else
		printf(", st_size=makedev\\(%u, %u\\)",
		       (unsigned int) major(stb.st_size),
		       (unsigned int) minor(stb.st_size));
#endif
		break;
	default:
		printf(", st_size=%llu", (unsigned long long) stb.st_size);
	}

	printf(", st_atime=");
		print_time(stb.st_atime);
	printf(", st_mtime=");
		print_time(stb.st_mtime);
	printf(", st_ctime=");
		print_time(stb.st_ctime);
	printf("(, st_flags=[0-9]+)?");
	printf("(, st_fstype=[^,]*)?");
	printf("(, st_gen=[0-9]+)?");
	printf("\\}");
#ifndef NR_stat
	printf("(, 0)?");
#endif
	printf("\\) += 0\n");
	return 0;
}

#else /* !STAT_PREFIX */
int main(void)
{
	return 77;
}
#endif