/* es.h -- definitions for higher order shell ($Revision: 1.2 $) */

#include "config.h"
#include "stdenv.h"

/*
 * meta-information for exported environment strings
 */

#define ENV_SEPARATOR	'\017'		/* control-O */
#define	ENV_ESCAPE	'\016'		/* control-N */


/*
 * the fundamental es data structures.
 */

typedef struct Tree Tree;
typedef struct Term Term;
typedef struct List List;
typedef struct Binding Binding;
typedef struct Closure Closure;

struct List {
	Term *term;
	List *next;
};

struct Binding {
	char *name;
	List *defn;
	Binding *next;
};

struct Closure {
	Binding	*binding;
	Tree *tree;
};


/*
 * parse trees
 */

typedef enum {
	nAssign, nCall, nClosure, nConcat, nFor, nLambda, nLet, nList, nLocal,
	nMatch, nExtract, nPrim, nQword, nThunk, nVar, nVarsub, nWord,
	nRedir, nPipe		/* only appear during construction */
} NodeKind;

struct Tree {
	NodeKind kind;
	union {
		Tree *p;
		char *s;
		int i;
	} u[2];
};


/*
 * miscellaneous data structures
 */

typedef struct StrList StrList;
struct StrList {
	char *str;
	StrList *next;
};

typedef struct {
	int alloclen, count;
	char *vector[1];
} Vector;			/* environment or arguments */


/*
 * our programming environment
 */


/* main.c */

#if GCVERBOSE
extern Boolean gcverbose;		/* -G */
#endif
#if GCINFO
extern Boolean gcinfo;			/* -I */
#endif


/* initial.c (for es) or dump.c (for esdump) */

extern void runinitial(void);


/* fd.c */

extern void mvfd(int old, int new);
extern int newfd(void);

#define	UNREGISTERED	(-999)
extern void registerfd(int *fdp, Boolean closeonfork);
extern void unregisterfd(int *fdp);
extern void releasefd(int fd);
extern void closefds(void);

extern int fdmap(int fd);
extern int defer_mvfd(Boolean parent, int old, int new);
extern int defer_close(Boolean parent, int fd);
extern void undefer(int ticket);


/* term.c */

extern Term *mkterm(char *str, Closure *closure);
extern Term *mkstr(char *str);
extern char *getstr(Term *term);
extern Closure *getclosure(Term *term);
extern Term *termcat(Term *t1, Term *t2);
extern Boolean termeq(Term *term, const char *s);
extern Boolean isclosure(Term *term);


/* list.c */

extern List *mklist(Term *term, List *next);
extern List *reverse(List *list);
extern List *append(List *head, List *tail);
extern List *listcopy(List *list);
extern int length(List *list);
extern List *listify(int argc, char **argv);
extern Term *nth(List *list, int n);
extern List *sortlist(List *list);


/* tree.c */

extern Tree *mk(NodeKind VARARGS);


/* closure.c */

extern Closure *mkclosure(Tree *tree, Binding *binding);
extern Closure *extractbindings(Tree *tree);
extern Binding *mkbinding(char *name, List *defn, Binding *next);
extern Binding *reversebindings(Binding *binding);


/* eval.c */

extern Binding *bindargs(Tree *params, List *args, Binding *binding);
extern List *forkexec(char *file, List *list, Boolean inchild);
extern List *walk(Tree *tree, Binding *binding, int flags);
extern List *eval(List *list, Binding *binding, int flags);
extern List *eval1(Term *term, int flags);
extern List *pathsearch(Term *term);

extern unsigned long evaldepth, maxevaldepth;
#define	MINmaxevaldepth		100
#define	MAXmaxevaldepth		0xffffffffU

#define	eval_inchild		1
#define	eval_exitonfalse	2
#define	eval_flags		(eval_inchild|eval_exitonfalse)


/* glom.c */

extern List *glom(Tree *tree, Binding *binding, Boolean globit);
extern List *glom2(Tree *tree, Binding *binding, StrList **quotep);


/* glob.c */

extern char QUOTED[], UNQUOTED[];
extern List *glob(List *list, StrList *quote);
extern Boolean haswild(const char *pattern, const char *quoting);


/* match.c */
extern Boolean match(const char *subject, const char *pattern, const char *quote);
extern Boolean listmatch(List *subject, List *pattern, StrList *quote);
extern List *extractmatches(List *subjects, List *patterns, StrList *quotes);


/* var.c */

extern void initvars(void);
extern void initenv(char **envp, Boolean protected);
extern void hidevariables(void);
extern void validatevar(const char *var);
extern List *varlookup(const char *name, Binding *binding);
extern List *varlookup2(char *name1, char *name2, Binding *binding);
extern void vardef(char *, Binding *, List *);
extern Vector *mkenv(void);
extern void setnoexport(List *list);
extern void addtolist(void *arg, char *key, void *value);
extern List *listvars(Boolean internal);

typedef struct Push Push;
extern Push *pushlist;
extern void varpush(Push *, char *, List *);
extern void varpop(Push *);


/* status.c */

extern List *true, *false;
extern Boolean istrue(List *status);
extern int exitstatus(List *status);
extern char *mkstatus(int status);
extern void printstatus(int pid, int status);


/* access.c */

extern char *checkexecutable(char *file);


/* proc.c */

extern Boolean hasforked;
extern int efork(Boolean parent, Boolean background);
extern int ewait(int pid, Boolean interruptible, void *rusage);
#define	ewaitfor(pid)	ewait(pid, FALSE, NULL)


/* dict.c */

typedef struct Dict Dict;
extern Dict *mkdict(void);
extern void dictforall(Dict *dict, void (*proc)(void *, char *, void *), void *arg);
extern void *dictget(Dict *dict, const char *name);
extern Dict *dictput(Dict *dict, char *name, void *value);
extern void *dictget2(Dict *dict, const char *name1, const char *name2);


/* conv.c */

extern void initconv(void);


/* print.c -- see print.h for more */

extern int print(const char *fmt VARARGS);
extern int eprint(const char *fmt VARARGS);
extern int fprint(int fd, const char *fmt VARARGS);
extern noreturn panic(const char *fmt VARARGS);


/* str.c */

extern char *str(const char *fmt VARARGS);	/* create a gc space string by printing */
extern char *mprint(const char *fmt VARARGS);	/* create an ealloc space string by printing */
extern StrList *mkstrlist(char *, StrList *);


/* vec.c */

extern Vector *mkvector(int n);
extern Vector *vectorize(List *list);
extern void sortvector(Vector *v);


/* util.c */

extern char *esstrerror(int err);
extern void uerror(char *msg);
extern void *ealloc(size_t n);
extern void *erealloc(void *p, size_t n);
extern void efree(void *p);
extern void ewrite(int fd, const char *s, size_t n);
extern long eread(int fd, char *buf, size_t n);
extern Boolean isabsolute(char *path);
extern Boolean streq2(const char *s, const char *t1, const char *t2);


/* input.c */

extern char *prompt, *prompt2;
extern Tree *parse(char *esprompt1, char *esprompt2);
extern Tree *parsestring(const char *str);
extern void sethistory(char *file);
extern Boolean isinteractive(void);
extern void initinput(void);
extern void resetparser(void);

extern List *runfd(int fd, const char *name, int flags);
extern List *runstring(const char *str, const char *name, int flags);

/* eval_* flags are also understood as runflags */
#define	run_interactive		 4	/* -i or $0[0] = '-' */
#define	run_noexec		 8	/* -n */
#define	run_echoinput		16	/* -v */
#define	run_printcmds		32	/* -x */
#define	run_lisptrees		64	/* -L and defined(LISPTREES) */

#if READLINE
extern Boolean resetterminal;
#endif


/* opt.c */

extern void esoptbegin(List *list, const char *caller, const char *usage);
extern int esopt(const char *options);
extern Term *esoptarg(void);
extern List *esoptend(void);


/* prim.c */

extern List *prim(char *s, List *list, Binding *binding, int evalflags);
extern void initprims(void);


/* split.c */

extern void startsplit(const char *sep, Boolean coalesce);
extern void splitstring(char *in, size_t len, Boolean endword);
extern List *endsplit(void);
extern List *fsplit(const char *sep, List *list, Boolean coalesce);


/* signal.c */

extern int signumber(const char *name);
extern char *signame(int sig);
extern char *sigmessage(int sig);

#define	SIGCHK() sigchk()
typedef enum {
	sig_nochange, sig_catch, sig_default, sig_ignore, sig_noop, sig_special
} Sigeffect;
extern Sigeffect esignal(int sig, Sigeffect effect);
extern void setsigeffects(const Sigeffect effects[]);
extern void getsigeffects(Sigeffect effects[]);
extern List *mksiglist(void);
extern void initsignals(Boolean interactive, Boolean allowdumps);
extern Atomic slow, interrupted;
extern jmp_buf slowlabel;
extern Boolean sigint_newline;
extern void sigchk(void);
extern Boolean issilentsignal(List *e);
extern void setsigdefaults(void);
extern void blocksignals(void);
extern void unblocksignals(void);


/* open.c */

typedef enum { oOpen, oCreate, oAppend, oReadWrite, oReadCreate, oReadAppend } OpenKind;
extern int eopen(char *name, OpenKind k);


/* version.c */

extern const char * const version;


/* gc.c -- see gc.h for more */

typedef struct Tag Tag;
#define	gcnew(type)	((type *) gcalloc(sizeof (type), &(CONCAT(type,Tag))))

extern void *gcalloc(size_t n, Tag *t);		/* allocate n with collection tag t */
extern char *gcdup(const char *s);		/* copy a 0-terminated string into gc space */
extern char *gcndup(const char *s, size_t n);	/* copy a counted string into gc space */

extern void initgc(void);			/* must be called at the dawn of time */
extern void gc(void);				/* provoke a collection, if enabled */
extern void gcreserve(size_t nbytes);		/* provoke a collection, if enabled and not enough space */
extern void gcenable(void);			/* enable collections */
extern void gcdisable(void);			/* disable collections */
extern Boolean gcisblocked();			/* is collection disabled? */


/*
 * garbage collector tags
 */

typedef struct Root Root;
struct Root {
	void **p;
	Root *next;
};

extern Root *rootlist;

#if REF_ASSERTIONS
#define	refassert(e)	assert(e)
#else
#define	refassert(e)	NOP
#endif

#define	Ref(t, v, init) \
	if (0) ; else { \
		t v = init; \
		Root (CONCAT(v,__root__)); \
		(CONCAT(v,__root__)).p = (void **) &v; \
		(CONCAT(v,__root__)).next = rootlist; \
		rootlist = &(CONCAT(v,__root__))
#define	RefPop(v) \
		refassert(rootlist == &(CONCAT(v,__root__))); \
		refassert(rootlist->p == (void **) &v); \
		rootlist = rootlist->next;
#define RefEnd(v) \
		RefPop(v); \
	}
#define RefReturn(v) \
		RefPop(v); \
		return v; \
	}
#define	RefAdd(e) \
	if (0) ; else { \
		Root __root__; \
		__root__.p = (void **) &e; \
		__root__.next = rootlist; \
		rootlist = &__root__
#define	RefRemove(e) \
		refassert(rootlist == &__root__); \
		refassert(rootlist->p == (void **) &e); \
		rootlist = rootlist->next; \
	}

#define	RefEnd2(v1, v2)		RefEnd(v1); RefEnd(v2)
#define	RefEnd3(v1, v2, v3)	RefEnd(v1); RefEnd2(v2, v3)
#define	RefEnd4(v1, v2, v3, v4)	RefEnd(v1); RefEnd3(v2, v3, v4)

#define	RefPop2(v1, v2)		RefPop(v1); RefPop(v2)
#define	RefPop3(v1, v2, v3)	RefPop(v1); RefPop2(v2, v3)
#define	RefPop4(v1, v2, v3, v4)	RefPop(v1); RefPop3(v2, v3, v4)

#define	RefAdd2(v1, v2)		RefAdd(v1); RefAdd(v2)
#define	RefAdd3(v1, v2, v3)	RefAdd(v1); RefAdd2(v2, v3)
#define	RefAdd4(v1, v2, v3, v4)	RefAdd(v1); RefAdd3(v2, v3, v4)

#define	RefRemove2(v1, v2)		RefRemove(v1); RefRemove(v2)
#define	RefRemove3(v1, v2, v3)		RefRemove(v1); RefRemove2(v2, v3)
#define	RefRemove4(v1, v2, v3, v4)	RefRemove(v1); RefRemove3(v2, v3, v4)

extern void globalroot(void *addr);

/* struct Push -- varpush() placeholder */

struct Push {
	Push *next;
	char *name;
	List *defn;
	int flags;
	Root nameroot, defnroot;
};


/*
 * exception handling
 *
 *	ExceptionHandler
 *		... body ...
 *	CatchException (e)
 *		... catching code using e ...
 *	EndExceptionHandler
 *
 */

typedef struct Handler Handler;
struct Handler {
	Handler *up;
	Root *rootlist;
	Push *pushlist;
	unsigned long evaldepth;
	jmp_buf label;
};

extern Handler *tophandler, *roothandler;
extern List *exception;
extern void pophandler(Handler *handler);
extern noreturn throw(List *exc);
extern noreturn fail(const char *from, const char *name VARARGS);
extern void newchildcatcher(void);

#if DEBUG_EXCEPTIONS
extern List *raised(List *e);
#else
#define	raised(e)	(e)
#endif

#define ExceptionHandler \
	{ \
		Handler _localhandler; \
		_localhandler.rootlist = rootlist; \
		_localhandler.pushlist = pushlist; \
		_localhandler.evaldepth = evaldepth; \
		_localhandler.up = tophandler; \
		tophandler = &_localhandler; \
		if (!setjmp(_localhandler.label)) {
	
#define CatchException(e) \
			pophandler(&_localhandler); \
		} else { \
			List *e = raised(exception); \

#define CatchExceptionIf(condition, e) \
			if (condition) \
				pophandler(&_localhandler); \
		} else { \
			List *e = raised(exception); \

#define EndExceptionHandler \
		} \
	}
