// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define _cleanup_(f) __attribute__((cleanup(f)))
#define _unused_ __attribute__((unused))

#ifndef O_PATH
#define O_PATH 0
#endif

static inline void freep(void *p) { free(*(void **)p); }

static inline void closep(int *fdp) {
  if (*fdp >= 0)
    close(*fdp);
}

static inline void closedirp(DIR **dp) {
  if (*dp)
    closedir(*dp);
}

static inline void fclosep(FILE **fp) {
  if (*fp)
    fclose(*fp);
}

#define _cleanup_free_ _cleanup_(freep)
#define _cleanup_close_ _cleanup_(closep)
#define _cleanup_closedir_ _cleanup_(closedirp)
#define _cleanup_fclose_ _cleanup_(fclosep)

#define ELEMENTSOF(x) (sizeof(x) / sizeof((x)[0]))
#define streq(a, b) (strcmp((a), (b)) == 0)
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)

#define MAX(a, b)				\
  ({						\
    typeof(a) _a = (a);				\
    typeof(b) _b = (b);				\
    _a > _b ? _a : _b;				\
  })
#define MIN(a, b)				\
  ({						\
    typeof(a) _a = (a);				\
    typeof(b) _b = (b);				\
    _a < _b ? _a : _b;				\
  })

#define free_and_replace(a, b)			\
  ({						\
    typeof(a) *_a = &(a);			\
    typeof(b) *_b = &(b);			\
    free(*_a);					\
    *_a = *_b;					\
    *_b = NULL;					\
    0;						\
  })

extern int log_level;
extern char *arg_root;

#define LOG_DEBUG 0
#define LOG_INFO 1
#define LOG_WARNING 2
#define LOG_ERROR 3

#define log_full(lvl, fmt, ...)					\
  do {								\
    if ((lvl) >= log_level)					\
      fprintf(stderr, "seedfiles: " fmt "\n", ##__VA_ARGS__);	\
  } while (0)

#define log_debug(fmt, ...) log_full(LOG_DEBUG, fmt, ##__VA_ARGS__)
#define log_info(fmt, ...) log_full(LOG_INFO, fmt, ##__VA_ARGS__)
#define log_warning(fmt, ...) log_full(LOG_WARNING, fmt, ##__VA_ARGS__)
#define log_error(fmt, ...) log_full(LOG_ERROR, fmt, ##__VA_ARGS__)

#define log_error_errno(e, fmt, ...)		\
  ({						\
    int _e = (e);				\
    log_error(fmt, ##__VA_ARGS__);		\
    _e;						\
  })

const char *startswith(const char *s, const char *prefix);
bool endswith(const char *s, const char *suffix);
char *strstrip(char *s);

static inline char *xstrdup(const char *s) {
  if (!s)
    return NULL;
  char *r = strdup(s);
  if (!r)
    abort();
  return r;
}

static inline char *xstrndup(const char *s, size_t n) {
  if (!s)
    return NULL;
  char *r = strndup(s, n);
  if (!r)
    abort();
  return r;
}

char *xasprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
int cunescape(const char *s, char **ret);

enum {
  EXTRACT_RELAX = 1 << 0,
  EXTRACT_CUNESCAPE = 1 << 1,
  EXTRACT_UNQUOTE = 1 << 2,
  EXTRACT_RETAIN_ESCAPE = 1 << 3,
};

int extract_first_word(const char **p, char **ret, const char *separators,
                       int flags);

static inline bool path_is_absolute(const char *p) { return p && p[0] == '/'; }

char *path_simplify(char *p);
char *path_join(const char *dir, const char *file);
const char *path_startswith(const char *path, const char *prefix);
char *path_parent(const char *path);
const char *path_basename(const char *path);

#define USEC_PER_SEC ((uint64_t)1000000ULL)
#define USEC_PER_MSEC ((uint64_t)1000ULL)
#define USEC_PER_MIN (USEC_PER_SEC * 60ULL)
#define USEC_PER_HOUR (USEC_PER_MIN * 60ULL)
#define USEC_PER_DAY (USEC_PER_HOUR * 24ULL)
#define USEC_PER_WEEK (USEC_PER_DAY * 7ULL)
#define USEC_INFINITY UINT64_MAX

int parse_duration(const char *s, uint64_t *ret);

int parse_uid(const char *s, uid_t *ret);
int parse_gid(const char *s, gid_t *ret);
int resolve_user(const char *name, uid_t *ret);
int resolve_group(const char *name, gid_t *ret);
int parse_mode(const char *s, mode_t *ret);

static inline int safe_close(int fd) {
  if (fd >= 0)
    close(fd);
  return -1;
}

int safe_open(const char *path, int flags, mode_t mode, unsigned expected_type);
int open_dir(const char *path);
int open_dir_at(int dir_fd, const char *path);
int open_parent(const char *path, const char **ret_basename);
DIR *xfdopendir(int fd);
int mkdirat_safe(int dir_fd, const char *name, mode_t mode);
int read_file_full(const char *path, char **ret, size_t *ret_size);
int read_fd_full(int fd, char **ret, size_t *ret_size);

int rm_rf_children(int dir_fd);
int rm_rf(const char *path);

static inline uint64_t now_usec(void) {
  struct timespec ts;
  clock_gettime(CLOCK_REALTIME, &ts);
  return (uint64_t)ts.tv_sec * USEC_PER_SEC + (uint64_t)ts.tv_nsec / 1000;
}

const char *get_boot_id(void);
const char *get_machine_id(void);

ssize_t readlink_dynamic(const char *restrict path, char **buf);
ssize_t readlinkat_dynamic(int dirfd, const char *restrict path, char **buf);
