/*
 * libdpkg - Debian packaging suite library routines
 * dpkg-db.h - declarations for in-core package database management
 *
 * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
 * Copyright © 2000,2001 Wichert Akkerman
 * Copyright © 2006-2014 Guillem Jover <guillem@debian.org>
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef LIBDPKG_DPKG_DB_H
#define LIBDPKG_DPKG_DB_H

#include <sys/types.h>

#include <stdbool.h>
#include <stdio.h>

#include <dpkg/macros.h>
#include <dpkg/varbuf.h>
#include <dpkg/version.h>
#include <dpkg/arch.h>

DPKG_BEGIN_DECLS

/**
 * @defgroup dpkg-db In-core package database management
 * @ingroup dpkg-public
 * @{
 */

enum deptype {
	dep_suggests,
	dep_recommends,
	dep_depends,
	dep_predepends,
	dep_breaks,
	dep_conflicts,
	dep_provides,
	dep_replaces,
	dep_enhances
};

struct dependency {
	struct pkginfo *up;
	struct dependency *next;
	struct deppossi *list;
	enum deptype type;
};

struct deppossi {
	struct dependency *up;
	struct pkgset *ed;
	struct deppossi *next, *rev_next, *rev_prev;
	const struct dpkg_arch *arch;
	struct dpkg_version version;
	enum dpkg_relation verrel;
	bool arch_is_implicit;
	bool cyclebreak;
};

struct arbitraryfield {
	struct arbitraryfield *next;
	const char *name;
	const char *value;
};

enum DPKG_ATTR_ENUM_FLAGS conffile_flags {
	/** No flags. */
	CONFFILE_NONE			= 0,
	/** The conffile is not shipped anymore in the new package. */
	CONFFILE_OBSOLETE		= DPKG_BIT(0),
	/** The conffile is marked to be removed during package upgrade. */
	CONFFILE_REMOVE_ON_UPGRADE	= DPKG_BIT(1),
};

struct conffile {
	struct conffile *next;
	const char *name;
	const char *hash;
	enum conffile_flags flags;
};

struct archivedetails {
	struct archivedetails *next;
	const char *name;
	const char *size;
	const char *md5sum;
};

enum pkgmultiarch {
	PKG_MULTIARCH_NO,
	PKG_MULTIARCH_SAME,
	PKG_MULTIARCH_ALLOWED,
	PKG_MULTIARCH_FOREIGN,
};

/**
 * Node describing a binary package file.
 *
 * This structure holds information contained on each binary package.
 */
struct pkgbin {
	struct dependency *depends;
	/** The ‘essential’ flag, true = yes, false = no (absent). */
	bool essential;
	/** The ‘protected’ flag, true = yes, false = no (absent). */
	bool is_protected;
	enum pkgmultiarch multiarch;
	const struct dpkg_arch *arch;
	/** The following is the "pkgname:archqual" cached string, if this was a
	 * C++ class this member would be mutable. */
	const char *pkgname_archqual;
	const char *description;
	const char *maintainer;
	const char *source;
	const char *installedsize;
	const char *origin;
	const char *bugs;
	struct dpkg_version version;
	struct conffile *conffiles;
	struct arbitraryfield *arbs;
};

/**
 * Node indicates that parent's Triggers-Pending mentions name.
 *
 * Note: These nodes do double duty: after they're removed from a package's
 * trigpend list, references may be preserved by the trigger cycle checker
 * (see trigproc.c).
 */
struct trigpend {
	struct trigpend *next;
	const char *name;
};

/**
 * Node indicates that aw's Triggers-Awaited mentions pend.
 */
struct trigaw {
	struct pkginfo *aw, *pend;
	struct trigaw *samepend_next;
	struct {
		struct trigaw *next, *prev;
	} sameaw;
};

/* Note: dselect and dpkg have different versions of this. */
struct perpackagestate;

enum pkgwant {
	PKG_WANT_UNKNOWN,
	PKG_WANT_INSTALL,
	PKG_WANT_HOLD,
	PKG_WANT_DEINSTALL,
	PKG_WANT_PURGE,
	/** Not allowed except as special sentinel value in some places. */
	PKG_WANT_SENTINEL,
};

enum pkgeflag {
	PKG_EFLAG_OK		= 0,
	PKG_EFLAG_REINSTREQ	= 1,
};

enum pkgstatus {
	PKG_STAT_NOTINSTALLED,
	PKG_STAT_CONFIGFILES,
	PKG_STAT_HALFINSTALLED,
	PKG_STAT_UNPACKED,
	PKG_STAT_HALFCONFIGURED,
	PKG_STAT_TRIGGERSAWAITED,
	PKG_STAT_TRIGGERSPENDING,
	PKG_STAT_INSTALLED,
};

enum pkgpriority {
	PKG_PRIO_REQUIRED,
	PKG_PRIO_IMPORTANT,
	PKG_PRIO_STANDARD,
	PKG_PRIO_OPTIONAL,
	PKG_PRIO_EXTRA,
	PKG_PRIO_OTHER,
	PKG_PRIO_UNKNOWN,
	PKG_PRIO_UNSET = -1,
};

/**
 * Node describing an architecture package instance.
 *
 * This structure holds state information.
 */
struct pkginfo {
	struct pkgset *set;
	struct pkginfo *arch_next;

	enum pkgwant want;
	/** The error flag bitmask. */
	enum pkgeflag eflag;
	enum pkgstatus status;
	enum pkgpriority priority;
	const char *otherpriority;
	const char *section;
	struct dpkg_version configversion;
	struct pkgbin installed;
	struct pkgbin available;
	struct perpackagestate *clientdata;

	struct archivedetails *archives;

	struct {
		/* ->aw == this */
		struct trigaw *head, *tail;
	} trigaw;

	/* ->pend == this, non-NULL for us when Triggers-Pending. */
	struct trigaw *othertrigaw_head;
	struct trigpend *trigpend_head;

	/**
	 * files_list_valid  files  Meaning
	 * ----------------  -----  -------
	 * false             NULL   Not read yet, must do so if want them.
	 * false             !NULL  Read, but rewritten and now out of date.
	 *                          If want info must throw away old and
	 *                          file reread.
	 * true              !NULL  Read, all is OK.
	 * true              NULL   Read OK, but, there were no files.
	 */
	struct fsys_namenode_list *files;
	off_t files_list_phys_offs;
	bool files_list_valid;

	/* The status has changed, it needs to be logged. */
	bool status_dirty;
};

/**
 * Node describing a package set sharing the same package name.
 */
struct pkgset {
	struct pkgset *next;
	const char *name;
	struct pkginfo pkg;
	struct {
		struct deppossi *available;
		struct deppossi *installed;
	} depended;
	int installed_instances;
};

/*** from dbdir.c ***/

const char *
dpkg_db_set_dir(const char *dir);
const char *
dpkg_db_get_dir(void);
char *
dpkg_db_get_path(const char *pathpart);

#include <dpkg/atomic-file.h>

/*** from dbmodify.c ***/

enum DPKG_ATTR_ENUM_FLAGS modstatdb_rw {
	msdbrw_unset			= -1,

	/* Those marked with «return» are possible returns from
	 * modstatdb_open(). */
	msdbrw_readonly,			/* «return» */
	msdbrw_needsuperuserlockonly,		/* «return» */
	msdbrw_writeifposs,
	msdbrw_write,				/* «return» */
	msdbrw_needsuperuser,

	/* Now some optional flags (starting at bit 8): */
	msdbrw_available_readonly	= DPKG_BIT(8),
	msdbrw_available_write		= DPKG_BIT(9),
	msdbrw_available_mask		= 0x0300,
};

void
modstatdb_init(void);
void
modstatdb_done(void);
bool
modstatdb_is_locked(void);
bool
modstatdb_can_lock(void);
void
modstatdb_lock(void);
void
modstatdb_unlock(void);
enum
modstatdb_rw modstatdb_open(enum modstatdb_rw reqrwflags);
enum
modstatdb_rw modstatdb_get_status(void);
void
modstatdb_note(struct pkginfo *pkg);
void
modstatdb_note_ifwrite(struct pkginfo *pkg);
void
modstatdb_checkpoint(void);
void
modstatdb_shutdown(void);

/*** from database.c ***/

void
pkgset_blank(struct pkgset *set);
int
pkgset_installed_instances(struct pkgset *set);

void
pkg_blank(struct pkginfo *pp);
void
pkgbin_blank(struct pkgbin *pkgbin);
bool
pkg_is_informative(struct pkginfo *pkg, struct pkgbin *info);

struct pkgset *
pkg_hash_find_set(const char *name);
struct pkginfo *
pkg_hash_get_singleton(struct pkgset *set);
struct pkginfo *
pkg_hash_find_singleton(const char *name);
struct pkginfo *
pkg_hash_get_pkg(struct pkgset *set, const struct dpkg_arch *arch);
struct pkginfo *
pkg_hash_find_pkg(const char *name, const struct dpkg_arch *arch);
int
pkg_hash_count_set(void);
int
pkg_hash_count_pkg(void);
void
pkg_hash_reset(void);

struct pkg_hash_iter *
pkg_hash_iter_new(void);
struct pkgset *
pkg_hash_iter_next_set(struct pkg_hash_iter *iter);
struct pkginfo *
pkg_hash_iter_next_pkg(struct pkg_hash_iter *iter);
void
pkg_hash_iter_free(struct pkg_hash_iter *iter);

void
pkg_hash_report(FILE *);

/*** from parse.c ***/

enum DPKG_ATTR_ENUM_FLAGS parsedbflags {
	/** Parse a single control stanza. */
	pdb_single_stanza		= DPKG_BIT(0),
	/** Store in ‘available’ in-core structures, not ‘status’. */
	pdb_recordavailable		= DPKG_BIT(1),
	/** Throw up an error if ‘Status’ encountered. */
	pdb_rejectstatus		= DPKG_BIT(2),
	/** Ignore priority/section info if we already have any. */
	pdb_weakclassification		= DPKG_BIT(3),
	/** Ignore archives info if we already have them. */
	pdb_ignore_archives		= DPKG_BIT(4),
	/** Ignore packages with older versions already read. */
	pdb_ignoreolder			= DPKG_BIT(5),
	/** Perform laxer version parsing. */
	pdb_lax_version_parser		= DPKG_BIT(6),
	/** Perform laxer control stanza parsing. */
	pdb_lax_stanza_parser		= DPKG_BIT(7),
	/** Perform laxer parsing, used to transition to stricter parsing. */
	pdb_lax_parser			= pdb_lax_stanza_parser |
					  pdb_lax_version_parser,
	/** Close file descriptor on context destruction. */
	pdb_close_fd			= DPKG_BIT(8),
	/** Interpret filename ‘-’ as stdin. */
	pdb_dash_is_stdin		= DPKG_BIT(9),
	/** Allow empty/missing files. */
	pdb_allow_empty			= DPKG_BIT(10),

	/* Standard operations. */

	pdb_parse_status	= pdb_lax_parser |
				  pdb_weakclassification |
				  pdb_allow_empty,
	pdb_parse_update	= pdb_parse_status |
				  pdb_single_stanza,
	pdb_parse_available	= pdb_recordavailable |
				  pdb_rejectstatus |
				  pdb_lax_parser |
				  pdb_allow_empty,
	pdb_parse_binary	= pdb_recordavailable |
				  pdb_rejectstatus |
				  pdb_single_stanza,
};

const char *
pkg_name_is_invalid(const char *p);

const struct fieldinfo *
find_field_info(const struct fieldinfo *fields, const char *fieldname);
const struct arbitraryfield *
find_arbfield_info(const struct arbitraryfield *arbs, const char *fieldname);

int
parsedb(const char *filename, enum parsedbflags, struct pkginfo **donep);
void
copy_dependency_links(struct pkginfo *pkg,
                      struct dependency **updateme,
                      struct dependency *newdepends,
                      bool available);

/*** from parsehelp.c ***/

#include <dpkg/namevalue.h>

extern const struct namevalue booleaninfos[];
extern const struct namevalue multiarchinfos[];
extern const struct namevalue priorityinfos[];
extern const struct namevalue statusinfos[];
extern const struct namevalue status_abbrev[];
extern const struct namevalue eflaginfos[];
extern const struct namevalue eflag_abbrev[];
extern const struct namevalue wantinfos[];
extern const struct namevalue want_abbrev[];

#include <dpkg/error.h>

enum versiondisplayepochwhen {
	vdew_never,
	vdew_nonambig,
	vdew_always,
};

void
varbufversion(struct varbuf *, const struct dpkg_version *,
              enum versiondisplayepochwhen);
int
parseversion(struct dpkg_version *version, const char *,
             struct dpkg_error *err);
const char *
versiondescribe(const struct dpkg_version *,
                enum versiondisplayepochwhen);
const char *
versiondescribe_c(const struct dpkg_version *version,
                  enum versiondisplayepochwhen vdew);

enum pkg_name_arch_when {
	/** Never display arch. */
	pnaw_never,
	/** Display arch only when it's non-ambiguous. */
	pnaw_nonambig,
	/** Display arch only when it's a Multi-Arch same package. */
	pnaw_same,
	/** Display arch only when it's a foreign one. */
	pnaw_foreign,
	/** Always display arch. */
	pnaw_always,
};

void
varbuf_add_pkgbin_name(struct varbuf *vb, const struct pkginfo *pkg,
                       const struct pkgbin *pkgbin,
                       enum pkg_name_arch_when pnaw);

const char *
pkgbin_name_archqual(const struct pkginfo *pkg, const struct pkgbin *pkgbin);

const char *
pkgbin_name(struct pkginfo *pkg, struct pkgbin *pkgbin,
            enum pkg_name_arch_when pnaw);
const char *
pkg_name(struct pkginfo *pkg, enum pkg_name_arch_when pnaw);

const char *
pkgbin_name_const(const struct pkginfo *pkg, const struct pkgbin *pkgbin,
                  enum pkg_name_arch_when pnaw);
const char *
pkg_name_const(const struct pkginfo *pkg, enum pkg_name_arch_when pnaw);

void
pkg_source_version(struct dpkg_version *version,
                   const struct pkginfo *pkg, const struct pkgbin *pkgbin);

void
varbuf_add_source_version(struct varbuf *vb,
                          const struct pkginfo *pkg,
                          const struct pkgbin *pkgbin);

const char *
pkg_want_name(const struct pkginfo *pkg);
const char *
pkg_status_name(const struct pkginfo *pkg);
const char *
pkg_eflag_name(const struct pkginfo *pkg);

const char *
pkg_priority_name(const struct pkginfo *pkg);

/*** from dump.c ***/

void
write_stanza(FILE *, const char *,
             const struct pkginfo *, const struct pkgbin *);

enum DPKG_ATTR_ENUM_FLAGS writedb_flags {
	/** No flags. */
	wdb_none			= 0,
	/** Dump ‘available’ in-core structures, not ‘status’. */
	wdb_dump_available		= DPKG_BIT(0),
	/** Must sync the written file. */
	wdb_must_sync			= DPKG_BIT(1),
};

void
writedb_stanzas(FILE *fp, const char *filename, enum writedb_flags flags);
void
writedb(const char *filename, enum writedb_flags flags);

/* Note: The varbufs must have been initialized and will not be
 * NUL-terminated. */
void
varbuf_stanza(struct varbuf *, const struct pkginfo *,
              const struct pkgbin *);
void
varbufdependency(struct varbuf *vb, struct dependency *dep);

/*** from depcon.c ***/

bool
versionsatisfied(struct pkgbin *it, struct deppossi *against);
bool
deparchsatisfied(struct pkgbin *it, const struct dpkg_arch *arch,
                      struct deppossi *against);
bool
archsatisfied(struct pkgbin *it, struct deppossi *against);

bool
pkg_virtual_deppossi_satisfied(struct deppossi *dependee,
                               struct deppossi *provider);

/*** from nfmalloc.c ***/

void *
nfmalloc(size_t);
char *
nfstrsave(const char*);
char *
nfstrnsave(const char*, size_t);
void
nffreeall(void);

/** @} */

DPKG_END_DECLS

#endif /* LIBDPKG_DPKG_DB_H */
