// Copyright © Tavian Barnes <tavianator@tavianator.com>
// SPDX-License-Identifier: 0BSD

/**
 * Directories and their contents.
 */

#ifndef BFS_DIR_H
#define BFS_DIR_H

#include "bfs.h"

#include <sys/types.h>

/**
 * Whether the implementation uses the getdents() syscall directly, rather than
 * libc's readdir().
 */
#ifndef BFS_USE_GETDENTS
#  if BFS_HAS_POSIX_GETDENTS
#    define BFS_USE_GETDENTS true
#  elif __linux__ || __FreeBSD__
#    define BFS_USE_GETDENTS (BFS_HAS_GETDENTS || BFS_HAS_GETDENTS64 | BFS_HAS_GETDENTS64_SYSCALL)
#  else
#    define BFS_USE_GETDENTS false
#  endif
#endif

/**
 * A directory.
 */
struct bfs_dir;

/**
 * File types.
 */
enum bfs_type {
	/** An error occurred for this file. */
	BFS_ERROR = -1,
	/** Unknown type. */
	BFS_UNKNOWN,
	/** Block device. */
	BFS_BLK,
	/** Character device. */
	BFS_CHR,
	/** Directory. */
	BFS_DIR,
	/** Solaris door. */
	BFS_DOOR,
	/** Pipe. */
	BFS_FIFO,
	/** Symbolic link. */
	BFS_LNK,
	/** Solaris event port. */
	BFS_PORT,
	/** Regular file. */
	BFS_REG,
	/** Socket. */
	BFS_SOCK,
	/** BSD whiteout. */
	BFS_WHT,
};

/**
 * Convert a bfs_stat() mode to a bfs_type.
 */
enum bfs_type bfs_mode_to_type(mode_t mode);

/**
 * A directory entry.
 */
struct bfs_dirent {
	/** The type of this file (possibly unknown). */
	enum bfs_type type;
	/** The name of this file. */
	const char *name;
};

/**
 * Allocate space for a directory.
 *
 * @return
 *         An allocated, unopen directory, or NULL on failure.
 */
struct bfs_dir *bfs_allocdir(void);

struct arena;

/**
 * Initialize an arena for directories.
 *
 * @arena
 *         The arena to initialize.
 */
void bfs_dir_arena(struct arena *arena);

/**
 * bfs_opendir() flags.
 */
enum bfs_dir_flags {
	/** Include whiteouts in the results. */
	BFS_DIR_WHITEOUTS = 1 << 0,
	/** @internal Start of private flags. */
	BFS_DIR_PRIVATE   = 1 << 1,
};

/**
 * Open a directory.
 *
 * @dir
 *         The allocated directory.
 * @at_fd
 *         The base directory for path resolution.
 * @at_path
 *         The path of the directory to open, relative to at_fd.  Pass NULL to
 *         open at_fd itself.
 * @flags
 *         Flags that control which directory entries are listed.
 * @return
 *         0 on success, or -1 on failure.
 */
int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path, enum bfs_dir_flags flags);

/**
 * Get the file descriptor for a directory.
 */
int bfs_dirfd(const struct bfs_dir *dir);

/**
 * Performs any I/O necessary for the next bfs_readdir() call.
 *
 * @dir
 *         The directory to poll.
 * @return
 *         1 on success, 0 on EOF, or -1 on failure.
 */
int bfs_polldir(struct bfs_dir *dir);

/**
 * Read a directory entry.
 *
 * @dir
 *         The directory to read.
 * @dirent[out]
 *         The directory entry to populate.
 * @return
 *         1 on success, 0 on EOF, or -1 on failure.
 */
int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *de);

/**
 * Close a directory.
 *
 * @return
 *         0 on success, -1 on failure.
 */
int bfs_closedir(struct bfs_dir *dir);

/**
 * Whether the bfs_unwrapdir() function is supported.
 */
#ifndef BFS_USE_UNWRAPDIR
#  define BFS_USE_UNWRAPDIR (BFS_USE_GETDENTS || BFS_HAS_FDCLOSEDIR)
#endif

#if BFS_USE_UNWRAPDIR
/**
 * Detach the file descriptor from an open directory.
 *
 * @dir
 *         The directory to detach.
 * @return
 *         The file descriptor of the directory.
 */
int bfs_unwrapdir(struct bfs_dir *dir);
#endif

#endif // BFS_DIR_H
