/*
 *	Editor engine
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *
 *	This file is part of JOE (Joe's Own Editor)
 */

/* A buffer is made up of a doubly-linked list of gap buffers.  Each gap buffer has a
 * a header.  The buffers themselves can be swapped out.  A gap buffer with
 * a pointer referring to it is guaranteed to be swapped in.
 */

struct header {
	LINK(H)	link;		/* Doubly-linked list of gap buffer headers */
	off_t	seg;		/* Swap file offset to gap buffer */
	short	hole;		/* Offset to gap */
	short	ehole;		/* Offset to after gap */
	short	nlines;		/* No. '\n's in this buffer */
	short	extra;		/* Unused */
};

/* It's a good idea to optimize the size of struct header since there is a header
 * for each page of loaded data.
 *
 * 32-bit ptr, 32-bit off: sizeof(H) == 20 bytes
 * 32-bit ptr, 64-bit off: sizeof(H) == 24 bytes
 * 64-bit ptr, 64-bit off: sizeof(H) == 32 bytes
 */

/* A pointer to some location within a buffer.  After an insert or delete,
 * all of the pointers following the insertion or deletion point are
 * adjusted so that they keep pointing to the same character. */

struct point {
	LINK(P)	link;		/* Doubly-linked list of pointers for a particular buffer */

	B	*b;		/* Buffer */
	short	ofst;		/* Gap buffer offset */
	char	*ptr;		/* Gap buffer address */
	H	*hdr;		/* Gap buffer header */

	off_t	byte;		/* Buffer byte offset */
	off_t	line;		/* Line number */
	off_t	col;		/* current column */
	off_t	xcol;		/* cursor column (can be different from actual column) */
	int	valcol;		/* bool: is col valid? */
	int	end;		/* set if this is end of file pointer */
	int	attr;		/* current ANSI attribute */
	int	valattr;	/* set if attr is still valid */

	P	**owner;	/* owner of this pointer.  owner gets cleared if pointer is deleted. */
	const char *tracker;	/* Name of function who pdup()ed me */
};

/* Options: both BWs and Bs have one of these */

struct options_match {
	struct options_match *next;
	const char	*name_regex; /* File name regex */
	const char	*contents_regex; /* File contents regex */
	struct regcomp	*r_contents_regex; /* Compiled version of context_regex */
};

struct options {
	OPTIONS	*next;
	const char *ftype; /* Name of this set of options */
	struct options_match *match; /* List of matching criteria */
	int	overtype;
	off_t	lmargin;
	off_t	rmargin;
	int	autoindent;
	int	wordwrap;
	int	nobackup;
	off_t	tab;
	int	indentc;
	off_t	istep;
	const char	*context;
	const char	*lmsg;
	const char	*rmsg;
	const char	*smsg;
	const char	*zmsg;
	int	linums;
	int	hiline;
	int	readonly;
	int	french;
	int	flowed;
	int	spaces;
	int	crlf;
	int	highlight;	/* Set to enable highlighting */
	int	visiblews;	/* Visible whitespace */
	int	syntax_debug;	/* Whether using OSC8 to help debug syntax colouring */
	const char *syntax_name;	/* Name of syntax to use */
	struct high_syntax *syntax;	/* Syntax for highlighting (load_syntax() from syntax_name happens in setopt()) */
	const char *map_name;	/* Name of character set */
	struct charmap *charmap;	/* Character set */
	const char *language;	/* Language of this buffer (for spell) */
	int	smarthome;	/* Set for smart home key */
	int	indentfirst;	/* Smart home goes to indentation point first */
	int	smartbacks;	/* Set for smart backspace key */
	int	purify;		/* Purify indentation */
	int	picture;	/* Picture mode */
	int	highlighter_context;	/* Use the context annotations from the highlighter for ^G */
	int	single_quoted;	/* Ignore '  ' for ^G */
	int	no_double_quoted;	/* Don't ignore " " for ^G */
	int	c_comment;	/* Ignore text in C comments */
	int	cpp_comment;	/* Ignore text after // comments */
	int	hash_comment;	/* Ignore text after # comments */
	int	vhdl_comment;	/* Ignore text after -- comments */
	int	semi_comment;	/* Ignore text after ; comments */
	int	tex_comment;	/* Ignore text after % comments */
	int	hex;		/* Hex edit mode */
	int	ansi;		/* Hide ANSI sequences mode */
	int	title;		/* Enable status line context display */
	const char *text_delimiters;	/* Define word delimiters */
	const char *cpara;	/* Characters which can indent paragraphs */
	const char *cnotpara;/* Characters which begin non-paragraph lines */
	MACRO	*mnew;		/* Macro to execute for new files */
	MACRO	*mold;		/* Macro to execute for existing files */
	MACRO	*msnew;		/* Macro to execute before saving new files */
	MACRO	*msold;		/* Macro to execute before saving existing files */
	MACRO	*mfirst;	/* Macro to execute on first change */
};

/* A buffer */

struct buffer {
	LINK(B)	link;		/* Doubly-linked list of all buffers */
	P	*bof;		/* Beginning of file pointer */
	P	*eof;		/* End of file pointer */
	char *name;	/* File name */
	int locked;		/* Set if we created a lock for this file */
	int ignored_lock;	/* Set if we didn't create a lock and we don't care (locked set in this case) */
	int didfirst;		/* Set after user attempted first change */
	time_t	mod_time;	/* Last modification time for file */
	time_t	check_time;	/* Last time we checked the file on disk */
	int	gave_notice;	/* Set if we already gave file changed notice for this file */
	int	orphan;		/* Set if buffer is orphaned: refcount is bumped up by one in this case */
	int	count;		/* Reference count.  Buffer is deleted if brm decrements count to 0 */
	int	changed;
	int	backup;
	UNDO	*undo;
	P	*marks[11];	/* Bookmarks */
	OPTIONS	o;		/* Options */
	P	*oldcur;	/* Last cursor position before orphaning */
	P	*oldtop;	/* Last top screen position before orphaning */
	P	*err;		/* Last error line */
	char *current_dir;
	int shell_flag;		/* Set if last cursor position is same as vt cursor: if it is we keep it up to date */
	int	rdonly;		/* Set for read-only */
	int	internal;	/* Set for internal buffers */
	int	scratch;	/* Set for scratch buffers */
	int	er;		/* Error code when file was loaded */
	pid_t	pid;		/* Process id */
	int	out;		/* fd to write to process */
	VT	*vt;		/* video terminal emulator */
	int     raw;            /* just append data from shell, don't interpret it */
	struct lattr_db *db;	/* Linked list of line attribute databases */
	void (*parseone)(struct charmap *map,const char *s,char **rtn_name,
	                 off_t *rtn_line);
	                        /* Error parser for this buffer */
};

extern B bufs;

/* 31744 */
extern char stdbuf[stdsiz];	/* Convenient global buffer */

extern int force;		/* Set to have final '\n' added to file */

extern VFILE *vmem;		/* Virtual memory file used for buffer system */

extern const char *const msgs[];	/* File access status messages */

void brm(B *b);
void brmall(void);
B *bmk(B *prop) ATTR_MALLOC(brm, 1);

B *bfind(const char *s);
B *bfind_scratch(const char *s);
B *bcheck_loaded(const char *s);
B *bfind_reload(const char *s);

void prm(P *p);

P *pdup(P *p, const char *tr) ATTR_MALLOC(prm, 1);
P *pdupown(P *p, P **o, const char *tr) ATTR_MALLOC(prm, 1);
P *poffline(P *p);
P *ponline(P *p);
B *bonline(B *b);
B *boffline(B *b);

P *pset(P *n, P *p);

P *p_goto_bof(P *p);		/* move cursor to begging of file */
P *p_goto_eof(P *p);		/* move cursor to end of file */
P *p_goto_bol(P *p);		/* move cursor to begging of line */
P *p_goto_eol(P *p);		/* move cursor to end of line */

P *p_goto_indent(P *p,int c);	/* move cursor to indentation point */

int pisbof(P *p);
int piseof(P *p);
int piseol(P *p);
int pisbol(P *p);
int pisbow(P *p);
int piseow(P *p);

#define piscol(p) ((p)->valcol ? (p)->col : (pfcol(p), (p)->col))

int pisblank(P *p);
int piseolblank(P *p);

off_t pisindent(P *p);
int pispure(P *p,int c);

int pnext(P *p);
int pprev(P *p);

int pgetb(P *p);
int prgetb(P *p);

int pgetc(P *p);
int prgetc(P *p);

P *pgoto(P *p, off_t loc);
P *pfwrd(P *p, off_t n);
P *pbkwd(P *p, off_t n);

P *pfcol(P *p);

P *pnextl(P *p);
P *pprevl(P *p);

P *pline(P *p, off_t line);

P *pcolwse(P *p, off_t goalcol);
P *pcol(P *p, off_t goalcol);
P *pcoli(P *p, off_t goalcol);
void pbackws(P *p);
void pfill(P *p, off_t to, int usetabs);

P *pfind(P *p, const char *s, ptrdiff_t len);
P *pifind(P *p, const char *s, ptrdiff_t len);
P *prfind(P *p, const char *s, ptrdiff_t len);
P *prifind(P *p, const char *s, ptrdiff_t len);

/* copy text between 'from' and 'to' into new buffer */
B *bcpy(P *from, P *to);

void pcoalesce(P *p);

void bdel(P *from, P *to);

/* insert buffer 'b' into another at 'p' */
P *binsb(P *p, B *b);
/* insert a block 'blk' of size 'amnt' into buffer at 'p' */
P *binsm(P *p, const char *blk, ptrdiff_t amnt);

/* Quoted insert.. */
P *binsmq(P *p, const char *blk, ptrdiff_t amnt);

/* insert character 'c' into buffer at 'p' */
P *binsc(P *p, int c);

/* insert byte 'c' into buffer at at 'p' */
P *binsbyte(P *p, char c);

/* insert zero term. string 's' into buffer at 'p' */
P *binss(P *p, const char *s);

/* B *bload(char *s);
 * Load a file into a new buffer
 *
 * Returns with errno set to 0 for success,
 * -1 for new file (file doesn't exist)
 * -2 for read error
 * -3 for seek error
 * -4 for open error
 */
B *bload(const char *s);
B *bread(int fi, off_t max, int binary);
B *borphan(void);

/* Save 'size' bytes beginning at 'p' into file with name in 's' */
int bsave(P *p, const char *s, off_t size,int flag);
int bsavefd(P *p, int fd, off_t size);

char *parsens(const char *s, off_t *skip, off_t *amnt, int *binary);

char *canonical(char *s, int flags);
#define CANFLAG_NORESTART 1 /* Support path restart feature */

/* Get byte at pointer or return NO_MORE_DATA if pointer is at end of buffer */
int brc(P *p);

/* Get character at pointer or return NO_MORE_DATA if pointer is at end of buffer */
int brch(P *p);

/* Copy 'size' bytes from a buffer beginning at p into block 'blk' */
char *brmem(P *p, char *blk, ptrdiff_t size);

/* Copy 'size' bytes from a buffer beginning at p into a zero-terminated
 * C-string in an malloc block.
 */
char *brs(P *p, ptrdiff_t size);

/* Copy 'size' bytes from a buffer beginning at p into a variable length string. */
char *brvs(P *p, ptrdiff_t size);

/* Copy line into buffer.  Maximum of size bytes will be copied.  Buffer needs
   to be one bigger for NIL */
char *brzs(P *p, char *buf, ptrdiff_t size);

B *bnext(void);
B *bafter(B *b);
B *bprev(void);

extern int berror;	/* bload error status code (use msgs[-berror] to get message) */

char **getbufs(void);

int lock_it(const char *path,char *buf);
void unlock_it(const char *path);
int plain_file(B *b);
int check_mod(B *b);
int file_exists(const char *path);

int udebug_joe(W *w, int k);

extern int guesscrlf; /* Try to guess line ending when set */
extern int guessindent; /* Try to guess indent character and step when set */
extern int break_links; /* Break hard links on write */
extern int break_symlinks; /* Break symbolic links on write */
extern int nodeadjoe; /* Prevent creation of DEADJOE files */

void set_file_pos_orphaned(void);

void breplace(B *b, B *n);

char *dequote(const char *);

#define ANSI_BIT (int)(0x80000000)
int ansi_code(char *s);
char *ansi_string(int code);

extern int guess_utf16;

/* Return true if filename will be interpreted as something other than a filename */
int hack_check(const char *name);
