/*
** This is the main header file for gentoo. Most source files will include this header,
** since it declares many of the data types that are used. When including this header,
** it should always be the first included file. The preferred order of inclusion is:
** #include "gentoo.h"
** #include <system stuff> (e.g. <stdio.h>, <stdlib.h>, ...)
** #include "more gentoo stuff" (e.g. "fileutil.h", "xmlutil.h")
** #include "me.h" (for a module named "me.c", that is).
**
** The reason why this file should be the first included one is that it fixes some
** system-dependant things which need to go before any system headers are included.
*/

#if !defined(__alpha)		/* I've heard this helps on Alpha systems... */
#define __EXTENSIONS__
#define _POSIX_C_SOURCE	3	/* This is for Solaris. */
#define	POSIX_C_SOURCE	3

#define	_BSD_SOURCE		/* For MAXNAMLEN on Linux. */

#endif				/* __alpha */

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <dirent.h>
#include <unistd.h>
#include <regex.h>

#if defined(__linux)
#include <sys/vfs.h>
#elif defined(__OpenBSD__)
#include <sys/param.h>
#include <sys/mount.h>
#else
#include <sys/statvfs.h>
#endif

#define	GTK_DISABLE_COMPAT_H		/* No GTK+ 1.0.6-compatibility required. */

#include <gtk/gtk.h>
#include <glib.h>

/* Not all systems' <mman.h> seem to define this. */
#if !defined(MAP_FAILED)
#define	MAP_FAILED	((void *) -1)
#endif

/* ----------------------------------------------------------------------------------------- */

/* The overall gentoo release version. Standard major.minor.micro format. The minor
** component will be odd for development versions.
*/
#define	VERSION	"0.11.7"

#define	RCNAME	".gentoorc"

/* ----------------------------------------------------------------------------------------- */

typedef struct _MainInfo	MainInfo;
typedef struct _DirPane		DirPane;
typedef struct _CmdSeq		CmdSeq;

/* These are various subsystems whose includes we need here, since they define datatypes
** that are needed below. This is a good point to include them, since MainInfo has been
** introduced (above).
*/
#include "keyboard.h"
#include "controls.h"
#include "iconutil.h"
#include "queue.h"
#include "cmdarg.h"
#include "styles.h"

#define	KEY_NAME_MAX	(32)

#define	CSQ_NAME_MAX	(32)

#define	FT_NAME_MAX	(32)
#define	FT_SUFFIX_MAX	(32)
#define	FT_NAMERE_MAX	(64)
#define	FT_FILERE_MAX	(64)

#define	FTFL_REQPERM	(1<<0)
#define	FTFL_REQSUFFIX	(1<<1)
#define	FTFL_NAMEMATCH	(1<<2)
#define	FTFL_NAMEGLOB	(1<<3)
#define	FTFL_FILEMATCH	(1<<4)
#define	FTFL_FILEGLOB	(1<<5)
#define	FTFL_NAMENOCASE	(1<<6)
#define	FTFL_FILENOCASE	(1<<7)

#define	FTPM_SETUID	(1<<0)
#define	FTPM_SETGID	(1<<1)
#define	FTPM_STICKY	(1<<2)
#define	FTPM_READ	(1<<3)
#define	FTPM_WRITE	(1<<4)
#define	FTPM_EXECUTE	(1<<5)

typedef struct {			/* Used to map stat() info onto names. */
	gchar	name[FT_NAME_MAX];		/* Human-readable name of filetype (e.g. "GIF image", "MP3 song"). */
	mode_t	mode;				/* Type flags, matched against stat()'s mode info. */
	guint32	flags;				/* Various flags. */
	guint32	perm;				/* Permissions to require. Not orthogonal with mode. */
	gchar	suffix[FT_SUFFIX_MAX];		/* Suffix to require (if FTFL_REQSUFFIX is set). */
	gchar	name_re_src[FT_NAMERE_MAX];	/* Regular expression to match against the name (if FTFL_MATCHNAME). */
	regex_t	*name_re;			/* Compiled version of the regular expression. */
	gchar	file_re_src[FT_FILERE_MAX];	/* RE to match against output of 'file' command (if FTFL_MATCHFILE). */
	regex_t	*file_re;			/* Again, a compiled version of the regular expression. */
	Style	*style;				/* Style to use for items matching this type. */
} FType;

#if 0

typedef enum {	PRTP_UNSEL_BGCOL,	PRTP_UNSEL_FGCOL,	PRTP_UNSEL_ICON,
		PRTP_LEGACY0,		PRTP_LEGACY1,		PRTP_LEGACY2,		/* Were SEL_xx visuals. */
		PRTP_ACTN_DEFAULT,	PRTP_ACTN_VIEW,		PRTP_ACTN_EDIT,
		PRTP_ACTN_PRINT,	PRTP_ACTN_PLAY,
		PRTP_NUM_TYPES } PrType;

#define	ICN_NAME_MAX	(64)

typedef struct {			/* A style property. These can be individually overridden. */
	PrType		type;			/* Type of this property. Determines contents of 'data'. */
	guint		override : 1;		/* If set, the property's value is legal and no inheritance done. */
	union {
	GdkColor	color;
	gchar		icon[ICN_NAME_MAX];
	GString		*action;
	} data;
} Prop;

#define	FS_NAME_MAX	(32)

struct _FStyle {			/* Style for a specific type of entry. Pointed at by filetype. */
	gchar	name[FS_NAME_MAX];		/* Human-readable name for style (e.g. "GIF image", "JPEG image"). */
	gint	expand : 1;			/* Is this node expanded or collapsed in config tree view? */
	gchar	pname[FS_NAME_MAX];		/* Name of the parent node. */
	FStyle	*parent;			/* Direct pointer at parent, for efficiency. */
	Prop	property[PRTP_NUM_TYPES];	/* Properties for this style. */
};

#endif

#define	DP_TITLE_MAX	(32)
#define	DP_FORMAT_MAX	(16)
#define	DP_DATEFMT_MAX	(32)
#define	DP_COLUMNS_MAX	(32)

typedef enum {	DPC_NAME, DPC_SIZE, DPC_BLOCKS, DPC_IQSIZE, DPC_MODENUM, DPC_MODESTR, DPC_NLINK,
		DPC_UIDNUM, DPC_UIDSTR, DPC_GIDNUM, DPC_GIDSTR, DPC_DEVICE, DPC_DEVMAJ, DPC_DEVMIN,
		DPC_ATIME, DPC_MTIME, DPC_CTIME,
		DPC_TYPE, DPC_ICON,
		DPC_NUM_TYPES } DPContent;

typedef struct {
	gint	show_type : 1;			/* Append type-character (from "@ / * | =") to names? */
	gint	show_linkname : 1;		/* Append " -> destination" on symbolic links? */
} DC_Name;

typedef struct {
	gchar	format[DP_FORMAT_MAX];		/* Formatting string for size. */
	gint	dir_show_fs_size : 1;		/* Show filesystem size for directories? */
} DC_Size;

typedef struct {
	gint	dir_show_fs_size : 1;		/* The same as for regular size, of course. */
} DC_IQSz;

typedef struct {
	gchar	format[DP_FORMAT_MAX];		/* General numerical formatter (for size, mode, uid, etc). */
} DC_Fmt;

typedef struct {
	gchar	format[DP_DATEFMT_MAX];		/* A strftime() format specifier. */
} DC_Time;

typedef union {
	DC_Name	name;
	DC_Size	size;
	DC_Fmt	blocks;
	DC_IQSz	iqsize;
	DC_Fmt	mode, nlink, uidnum, gidnum, device, devmaj, devmin;
	DC_Time	a_time, m_time, c_time;		/* Accessed, modified & changed, right? */
} DpCExtra;

typedef struct {
	gchar			title[DP_TITLE_MAX];
	DPContent		content;
	DpCExtra		extra;		/* Content-specific flags. */
	GtkJustification	just;
	gint			width;
} DpCFmt;

typedef enum { DPS_DIRS_FIRST = 0, DPS_DIRS_LAST, DPS_DIRS_MIXED } SortMode;

typedef struct {
	guint		column;		/* Index of column to sort on. */
	SortMode	mode;		/* Controls placement of directories. */
	gint		invert : 1;
	gint		nocase : 1;		/* Set for case-insensitive string comparisons. Lame! */
} DPSort;

typedef enum { SBP_IGNORE = 0, SBP_LEFT, SBP_RIGHT } SBarPos;

/* Click mode -- determines if (and how) we alter the CONTROL state for pane clicks. */
typedef enum { CTM_IGNORE = 0, CTM_SET, CTM_RESET, CTM_TOGGLE } CtrlMode;

typedef struct {
	guint		num_columns;
	DpCFmt		format[DP_COLUMNS_MAX];
	DPSort		sort;
	gchar		def_path[PATH_MAX];
	gint		path_above : 1;		/* Set to get the path entry above the actual pane. */
	gint		hide_allowed : 1;	/* Set to enable hiding (default). */
	gint		scrollbar_always : 1;	/* Set to always show scrollbar, regardless of # of entries. */
	SBarPos		sbar_pos;		/* Position of scrollbar. */
	CtrlMode	ctrl_mode;		/* Controls altering of the Control key state for pane clicks. */
} DPFormat;

typedef enum { DPCOL_PANE_SELECTED = 0, DPCOL_FOCUS_UNSELECTED, DPCOL_FOCUS_SELECTED, DPCOL_NUM } DpCol;

typedef struct {
	GdkColor	color[DPCOL_NUM];
} DPColors;

/* ----------------------------------------------------------------------------------------- */

/* General command row flags. */
#define	CGF_RUNINBG		(1<<0)		/* Run in command in background? */
#define	CGF_KILLPREV		(1<<1)		/* Kill previous instance of program (only background)? */
#define	CGF_GRABOUTPUT		(1<<2)		/* Capture the output into a special window? */
#define	CGF_SURVIVE		(1<<3)		/* Survive when gentoo quits (only background)? */

/* These flags are for the before- and after-flag fields. */
#define	CBAF_RESCAN_SOURCE	(1<<0)		/* Rescan the source dir pane? */
#define	CBAF_RESCAN_DEST	(1<<1)
#define	CBAF_CD_SOURCE		(1<<2)		/* These save some config work. */
#define	CBAF_CD_DEST		(1<<3)

typedef struct {		/* Extra info for external commands. */
	guint32	gflags;			/* General flags. */
	guint32	baflags[2];		/* Before and after flags. */
} CX_Ext;

typedef enum {	CRTP_BUILTIN, CRTP_EXTERNAL,
		CRTP_NUM_TYPES } CRType;


typedef struct {		/* A command "row". */
	CRType	type;			/* The type of the row. */
	GString	*def;			/* The row definition string. */
	guint32	flags;			/* Flags common to all types. */
	union {				/* Type-specific row info. */
	CX_Ext	external;		/* Extra info for external commands. */
	} extra;
} CmdRow;

#define	CSFLG_REPEAT	(1<<0)		/* Repeat sequence until no selection? */

struct _CmdSeq {
	gchar	name[CSQ_NAME_MAX];	/* Name of this command sequence, really. */
	guint32	flags;			/* Flags for this sequence. */
	GList	*rows;			/* List of CmdRow definition rows. */
};

typedef struct {			/* Information about commands live here. */
	GHashTable	*builtin;		/* Built-in commands (actually CmdDesc structs -- see cmdseq.c). */
	GHashTable	*cmdseq;		/* Command sequences. */
} CmdInfo;

/* ----------------------------------------------------------------------------------------- */

#define	BTN_LABEL_MAX	(32)
#define	BTN_TOOLTIP_MAX	(64)			/* Arbitrary, as always. */

typedef enum { SHP_CURRENT = 0, SHP_OTHER, SHP_LEFT, SHP_RIGHT } ShcPane;

typedef struct {			/* A shortcut is a button for rapid access to a path. Very Opus. */
	gchar	label[BTN_LABEL_MAX];		/* The label to display on the button, of course. */
	gchar	path[PATH_MAX];			/* Path associated with this shortcut button. */
	ShcPane	pane;				/* Which pane should the shortcut be read into? */
	gint	show_tooltip : 1;		/* Should tooltip (with the path) be shown? */
} Shortcut;

typedef enum { SSM_NONE = 0, SSM_SIMPLE, SSM_PANED } ShbSep;

typedef struct {			/* Main "container" datatype for the shortcuts. Keeps it tidy. */
	GList	*shortcut;			/* Just a simple glib list of Shortcuts (above). */
	ShbSep	sep_mode;			/* Separator mode. */
	gint	visible : 1;			/* Should the shortcuts be visible at all? */
	gint	right : 1;			/* To the right of the buttons? */
} ShortcutBank;

typedef struct {
	GList	*sheets;			/* List of button sheets. */
} ButtonInfo;

typedef struct {
	gchar	*text;
	gchar	*command;
} MenuItem;

typedef struct {
	MenuItem	*menuitem;
} MenuRoot;

typedef struct {			/* Options for overwrite-confirmation dialog module. */
	gint	show_info : 1;			/* Show info (sizes & dates) for conflicting files? */
	char	datefmt[DP_DATEFMT_MAX];	/* How to format dates? */
} OptOverwrite;

typedef enum { HIDE_NONE, HIDE_DOT, HIDE_REGEXP } HMode;

typedef struct {			/* Info about files hidden from user. Not ignored, just hidden. */
	HMode	mode;
	gchar	hide_re_src[MAXNAMLEN];		/* Regular expression; matches are hidden if mode == HIDE_REGEXP. */
	regex_t	*hide_re;			/* Compiled version of the RE. */
	gint	no_case : 1;			/* Ignore case in the RE? */
} HideInfo;

typedef enum {	PTID_ICON = 0, PTID_GTKRC, PTID_FSTAB, PTID_MTAB,
		PTID_NUM_PATHS } PathID;

typedef struct {				/* Miscellanous paths. */
	GString		*path[PTID_NUM_PATHS];		/* Just one GString per path, that's all. */
	HideInfo	hideinfo;			/* Info about which files should be hidden. */
} PathInfo;

/* This controls how often we do mounts, if ever. The levels are:
** MNT_NEVER	Disable all mounting.
** MNT_RARELY	Do mounting only "rarely". Currently means that mounting is done
**		when a dir is double-clicked, and when a shortcut is accessed.
** MNT_OFTEN	Does a mount on every directory scan. Might be slow.
*/
typedef enum { MNT_NEVER = 0, MNT_RARELY, MNT_OFTEN }	MntMode;

typedef struct {			/* Configuration info for automounting. */
	MntMode	mode;				/* Global mounting mode. [MNT_NEVER] */
	gint	check_nlink : 1;		/* Require nlink == 2 before mounting? [TRUE] */
	gint	show_stderr : 1;		/* Show stderr of mount/umount command? [TRUE] */
	gint	umount_exit : 1;		/* Unmount all mounted dirs at exit? [TRUE] */
	gchar	cmd_mount[MAXNAMLEN];		/* Full name to command to use for mounting. ["/bin/mount"] */
	gchar	cmd_umount[MAXNAMLEN];		/* This command is used for unmounting. ["/bin/umount"] */
} MountInfo;

#define	PSPF_TRACK	(1<<0)		/* Track changes to this pair? */
#define	PSPF_SET	(1<<1)		/* Set value from config on start-up? */
#define	PSPF_SAVE	(1<<2)		/* Grab current and save on exit? */

typedef struct {			/* A value pair used to hold root window position & size. */
	gint	a, b;
	guint32	flags;
} PSPair;

typedef struct {			/* This holds the actual info about window pos & size. */
	PSPair	pos;
	PSPair	size;
} WinInfo;

#define	CFLG_CHANGED	(1<<0)		/* Config has changed. Set on "OK", cleared by cfg_save_all(). */

typedef struct {			/* Holds all configuration info. */
	guint32		flags;
	GList		*type;			/* List of types (FType structures). */
	StyleInfo	*style;			/* Opaque style container. */
	DPFormat	dp_format[2];
	DPColors	dp_colors;		/* Global color information used to render dirpanes. */
	OptOverwrite	opt_overwrite;
	ShortcutBank	*shortcutbank;
	ButtonInfo	buttons;
	MenuRoot	dpmenuroot;		
	CmdInfo		commands;
	PathInfo	path;
	MountInfo	mount;
	WinInfo		wininfo;
	CtrlInfo	*ctrlinfo;
	gboolean	(*dir_filter)(const gchar *name);
} CfgInfo;

typedef struct _GuiInfo	GuiInfo;

struct _MainInfo {				/* gentoo's single most central data structure. */
	CfgInfo		cfg;
	GuiInfo		*gui;
	IconInfo	*ico;
	QueueInfo	*que;
};

/* -- Directory content data structures -- */

/* These macros are used to access fields of a DirRow structure. */
#define	DP_ROW_NAME(r)		((r)->dr_name)
#define	DP_ROW_LSTAT(r)		((r)->dr_lstat)
#define	DP_ROW_STAT(r)		(*((r)->dr_stat))
#define	DP_ROW_LINKNAME(r)	((r)->dr_linkname)
#define	DP_ROW_FLAGS(r)		((r)->dr_flags)
#define	DP_ROW_FLAGS_SET(r,f)	(DP_ROW_FLAGS(r) |= (f))
#define	DP_ROW_FLAGS_CLR(r,f)	(DP_ROW_FLAGS(r) &= ~(f))
#define	DP_ROW_FLAGS_CHK(r,f)	((DP_ROW_FLAGS(r) & (f)) == (f))
#define	DP_ROW_TYPE(r)		((r)->dr_type)
#define	DP_ROW_INDEX(p,r)	((r) - (p)->dir.line)

#define	DP_ROW_SET_STAT(r,s)	(((r)->dr_stat) = (s))
#define	DP_ROW_GET_STAT(r)	((r)->dr_stat)
#define	DP_ROW_SET_LINKNAME(r,n) ((r)->dr_linkname = (n))

/* These macros are to be used when accessing a DirRow through a selection list, which
** is the most frequent case. The 's' parameter is the SList iterator.
*/
#define	DP_SEL_ROW(s)		((DirRow *) (s)->data)
#define	DP_SEL_NAME(s)		(DP_ROW_NAME(DP_SEL_ROW(s)))
#define	DP_SEL_LSTAT(s)		(DP_ROW_LSTAT(DP_SEL_ROW(s)))
#define	DP_SEL_STAT(s)		(DP_ROW_STAT(DP_SEL_ROW(s)))
#define	DP_SEL_LINKNAME(s)	(DP_ROW_LINKNAME(DP_SEL_ROW(s)))
#define	DP_SEL_FLAGS(s)		(DP_ROW_FLAGS(DP_SEL_ROW(s)))
#define	DP_SEL_FLAGS_SET(s,f)	(DP_ROW_FLAGS_SET(DP_SEL_ROW(s), (f)))
#define	DP_SEL_FLAGS_CLR(s,f)	(DP_ROW_FLAGS_CLR(DP_SEL_ROW(s), (f)))
#define	DP_SEL_FLAGS_CHK(s,f)	(DP_ROW_FLAGS_CHK(DP_SEL_ROW(s), f))
#define	DP_SEL_TYPE(s)		(DP_ROW_TYPE(DP_SEL_ROW(s)))
#define	DP_SEL_INDEX(p,s)	(DP_ROW_INDEX((p), DP_SEL_ROW(s)))

#define	DPRF_HAS_SIZE		(1<<0)		/* Set when a row has a "real" size (not set for directories unless GetSize:d). */

typedef struct {			/* Representation of a single line in a directory (a file, typically). */
	gchar		*dr_name;		/* Name, points into big block in DirContents. */
	struct stat	dr_lstat;		/* From a call to lstat(). */
	gchar		*dr_linkname;		/* Contents of symbolic link (if S_ISLNK() true on dr_lstat). Else NULL. */
	struct stat	*dr_stat;		/* If link, this holds link target stats. Else, it points at dr_lstat. */
	guint32		dr_flags;		/* Misc. flags. */
	FType		*dr_type;		/* Type information. */
} DirRow;

typedef struct {
	guint		num_dirs, num_files;
	guint64		num_bytes, num_blocks;
} SelInfo;

typedef struct {			/* Some trivial file system information. Updated on rescan. */
	gboolean	valid;			/* Set if the structure's contents are valid. */
#if defined(__linux) || defined(__OpenBSD__)
	struct statfs	stat;			/* Holds information about filesystem. */
#else
	struct statvfs	stat;
#endif
} FsInfo;

typedef struct {
	gchar		path[PATH_MAX];		/* Contents apply to this path. */
	guint		num_lines;		/* Number of valid lines. */
	guint		tot_dirs, tot_files;	/* Total number of entries in directory. */
	guint64		tot_bytes, tot_blocks;	/* Sum of all sizes (in bytes & blocks). */
	DirRow		*line;			/* A bunch of lines. */
	gchar		*auxbuf;		/* This holds auxillary info (names, link names, and link stats). */
	SelInfo		sel;
	FsInfo		fs;
} DirContents;

/* -- Directory history stuff -- */

typedef struct {
	GList	*history;			/* Visited directories. Recent are close to head. */
} DirHistory;

/* -- Main DirPane structure --------------------------------------------------------------- */

struct _DirPane {
	MainInfo	*main;		/* Handy to have around. */
	gint		index;		/* Index of *this* pane, in the grand scheme of things. */
	GtkWidget	*vbox;
	GtkWidget	*path;		/* As of gentoo 0.9.14, this is a combo rather than an entry. */
	GtkWidget	*hide;		/* A toggle button showing (and controlling) hide status. */
	GtkWidget	*scwin;		/* Scrolling window for clist to live in. */
	GtkCList	*list;		/* The actual clist that shows all stuff. */
	DirContents	dir;		/* Contents of directory (list of names etc). */
	DirHistory	hist;		/* History over previously visited directories. */
	guint32		flags;		/* Various fun flags. */
	gint		last_row;	/* The last clicked row. */
	gint		last_row2;	/* The second last clicked row. */
	gint		dbclk_row;	/* The row that was double clicked, or -1. */
	gint		focus_row;	/* The last row to be rendered focused, or -1. */
	gint		old_focus_row;	/* Used to remember focus when pane is deactivated. */
};

/* -- Graphic user interface stuff --------------------------------------------------------- */

#define	DP_NUMBER	(2)		/* Number of panes. Should really be two. */

struct _GuiInfo {
	GtkWidget	*window;
	guint		win_del_evt;
	guint		win_sa_evt;
	GtkWidget	*vbox;			/* Vbox that contains entire GUI. */
	GtkLabel	*top;
	GtkWidget	*hpane;			/* Horizontal pane holding panes. */
	GtkWidget	*middle;		/* A box holding entire middle part. */
	GtkWidget	*bottom;		/* Bottom part of GUI. */
	GtkWidget	*dpmenu;		/* Menu for dirpanes. Very useful. */
	DirPane		pane[DP_NUMBER];
	DirPane		*cur_dir;
	KbdContext	*kbd_ctx;		/* Keyboard context, for shortcuts. */
};

/* ----------------------------------------------------------------------------------------- */

/* These are defined in the main "gentoo.c" module, but really shouldn't be. Until they
** get a module of their own, they need to be prototyped like this. :(
*/
void	rebuild_middle(MainInfo *min);
void	rebuild_bottom(MainInfo *min);
