/* SPDX-License-Identifier: GPL-3.0-only */

#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <fnmatch.h>
#include <regex.h>
#include <assert.h>
#include <lopsub.h>
#include <sys/uio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <grp.h>

#include "err.h"

/** Compute the minimum of \a x and \a y. */
#define MIN(x, y) ({ \
	typeof(x) _min1 = (x); \
	typeof(y) _min2 = (y); \
	(void) (&_min1 == &_min2); \
	_min1 < _min2 ? _min1 : _min2; })

/** Compute the maximum of \a x and \a y. */
#define MAX(x, y) ({ \
	typeof(x) _max1 = (x); \
	typeof(y) _max2 = (y); \
	(void) (&_max1 == &_max2); \
	_max1 < _max2 ? _max2 : _max1; })

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

/* util.c */

extern int loglevel_arg_val;
enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
void tf_log(int ll, const char* fmt,...);
#define DEBUG_LOG(f,...) tf_log(LL_DEBUG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define INFO_LOG(f,...) tf_log(LL_INFO, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define NOTICE_LOG(f,...) tf_log(LL_NOTICE, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define WARNING_LOG(f,...) tf_log(LL_WARNING, "%s: " f, __FUNCTION__, ##  __VA_ARGS__)
#define ERROR_LOG(f,...) tf_log(LL_ERROR, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define CRIT_LOG(f,...) tf_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
#define EMERG_LOG(f,...) tf_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)

int atoi64(const char *str, int64_t *value);
unsigned xvasprintf(char **result, const char *fmt, va_list ap);
unsigned xasprintf(char **result, const char *fmt, ...);
void *xrealloc(void *p, size_t size);
void *xmalloc(size_t size);
void *xcalloc(size_t size);
char *xstrdup(const char *str);
char *get_homedir(void);
int xregcomp(regex_t *preg, const char *regex, int cflags);
void mmap_file(const char *path, struct iovec *iov);
int fd2buf(int fd, struct iovec *result);

struct regfile_iter; /* opaque */
void regfile_iter_new(const char *dirname, struct regfile_iter **result);
bool regfile_iter_map(const struct regfile_iter *iter, struct iovec *result);
const char *regfile_iter_basename(const struct regfile_iter *iter);
const struct stat *regfile_iter_stat(const struct regfile_iter *iter);
void regfile_iter_next(struct regfile_iter *iter);
void regfile_iter_free(struct regfile_iter *iter);

/* tfortune.c */
struct epi_properties; /* opaque */
unsigned epi_len(const struct epi_properties *props);
bool epi_has_tag(const char *tag, const struct epi_properties *props);
char *epi_text(const struct epi_properties *props);

/* version.c */
const char *tf_version(void);

/* tag expression parser (ast.c, txp.lex, txp.y) */

/* Opaque, only known to ast.c. Passed to the generated txp_yyparse(). */
struct txp_context;

int txp_init(const struct iovec *definition, struct txp_context **result,
		 char **errmsg);
bool txp_eval_ast(const struct txp_context *ctx,
		const struct epi_properties *props);
void txp_free(struct txp_context *ctx);

/* non-public API of the tag expression parser */

/*
 * Since we use a reentrant lexer, all functions generated by flex(1)
 * receive an additional argument of this type.
 */
typedef void *txp_yyscan_t;

/* Parsed regex pattern. */
struct txp_re_pattern {
	regex_t preg; /* Pre-compiled regex. */
	unsigned flags; /* Subset of the cflags described in regex(3). */
};

/*
 * The possible values of a node in the abstract syntax tree (AST).
 *
 * Constant semantic values (string literals, numeric constants and regex
 * patterns which are part of the tag expression) are determined during
 * txp_init() while values which depend on the epigram (tags, number of lines,
 * etc.) are determined during txp_eval_row().
 *
 * This union, and the txp_ast_node structure below are used extensively in
 * txp.y. However, both need to be public because the lexer must be able to
 * create AST nodes for the constant semantic values.
 */
union txp_semantic_value {
	bool boolval; /* Comparators, =~ and =|. */
	char *strval; /* String literals (e.g., argument of tag()) */
	int64_t intval; /* Constants, num_lines, etc. */
	struct txp_re_pattern re_pattern; /* Right-hand side operand of =~. */
};

/*
 * A node is either interior or a leaf node. Interior nodes have at least one
 * child while leaf nodes have a semantic value and no children.
 *
 * Examples: (a) STRING_LITERAL has a semantic value (the unescaped string
 * literal) and no children, (b) NEG (unary minus) has no semantic value but
 * one child (the numeric expression that is to be negated), (c) LESS_OR_EQUAL
 * has no semantic value and two children (the two numeric expressions being
 * compared).
 */
struct txp_ast_node {
	/* Corresponds to a token type, for example LESS_OR_EQUAL. */
	int id;
	union {
		/* Pointers to the child nodes (interior nodes only). */
		struct txp_ast_node **children;
		/* Leaf nodes only. */
		union txp_semantic_value sv;
	};
	/*
	 * The number of children is implicitly given by the id, but we include
	 * it here to avoid having to maintain a lookup table. The AST is
	 * usually small, so we can afford to waste a byte per node.
	 */
	uint8_t num_children;
};

/* Called from both the lexer and the parser. */
__attribute__ ((format (printf, 3, 4)))
void txp_parse_error(int line, struct txp_context *ctx, const char *fmt, ...);

/* Helper functions for the lexer. */
unsigned parse_quoted_string(const char *src, const char quote_chars[2],
		char **result);
int txp_parse_regex_pattern(const char *src, struct txp_re_pattern *result);

struct txp_ast_node *ast_node_new_unary(int id, struct txp_ast_node *child);
struct txp_ast_node *ast_node_new_binary(int id, struct txp_ast_node *left,
		struct txp_ast_node *right);

struct txp_ast_node *txp_new_ast_leaf_node(int id);

/* linhash.c */

struct linhash_item {
	const char *key;
	void *object;
};

typedef int linhash_comparator(const struct linhash_item **a,
		const struct linhash_item **b);

struct linhash_table;
struct linhash_iterator;

struct linhash_table *linhash_new(uint32_t order);
int linhash_insert(struct linhash_item *item, struct linhash_table *t,
		void ***object);
struct linhash_item *linhash_lookup(const char *key,
		const struct linhash_table *t);
void *linhash_remove(const char *key, struct linhash_table *t);
void linhash_free(struct linhash_table *t);

struct linhash_iterator *linhash_iterator_new(struct linhash_table *t,
		linhash_comparator *comp, bool reverse);
struct linhash_item *linhash_iterator_item(const struct linhash_iterator *iter);
void linhash_iterator_next(struct linhash_iterator *iter);
void linhash_iterator_free(struct linhash_iterator *iter);

char *linhash_statistics(const struct linhash_table *t);
uint32_t linhash_num_items(const struct linhash_table *t);
