/* This file is part of Mailfromd.
   Copyright (C) 2006-2025 Sergey Poznyakoff

   This program 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 3, or (at your option)
   any later version.

   This program 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 <http://www.gnu.org/licenses/>. */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include "mailfromd.h"


#define BUCKETSIZE 16

struct symbucket {
	struct symbucket *next;
	size_t elsize;
	size_t index;
	char *buf;
};

struct _syment_align {
	char c;
	struct syment x;
};

#define __SYMENT_ALIGNMENT (offsetof(struct _syment_align, x))

#define __SYMENT_ALIGN(a, b) (((a) + (b) - 1) & ~((b) - 1))
#define SYMENT_SIZE(a) \
	__SYMENT_ALIGN((a)->elsize,__SYMENT_ALIGNMENT)
#define __SYMENT(a,p,n) \
	((struct syment*) ((char*) (p) + SYMENT_SIZE(a) * (n)))
#define SYMENT(a,n) \
	__SYMENT(a,(a)->buf,n)

static struct symbucket *symbucket;

/* Bucket handling */
static struct symbucket *
alloc_bucket(size_t elsize)
{
	size_t x;
	size_t size;
	struct symbucket *p;

	x = __SYMENT_ALIGN(elsize,__SYMENT_ALIGNMENT);
	size = ((sizeof(*p) + x - 1) / x) * x;
	p = mu_alloc(size + x * BUCKETSIZE);
	p->index = 0;
	p->elsize = elsize;
	p->buf = (char*)p + x;
	p->next = symbucket;
	return symbucket = p;
}

static struct symbucket *
find_bucket(size_t elsize)
{
	struct symbucket *sp;
	for (sp = symbucket; sp; sp = sp->next)
		if (sp->elsize == elsize && sp->index < BUCKETSIZE)
			return sp;
	return alloc_bucket(elsize);
}

static void *
alloc_entry(size_t size)
{
	struct symbucket *bp = find_bucket(size);
	size_t n = bp->index++;
	struct syment *sym = SYMENT(bp, n);
	memset(sym, 0, size);
	sym->refcnt = 1;
	return sym;
}

static void
free_entry(void *ptr)
{
	/* nothing */
}

static void
free_buckets()
{
	struct symbucket *p = symbucket;
	while (p) {
		struct symbucket *next = p->next;
		free(p);
		p = next;
	}
	symbucket = NULL;
}


/* Import rules */
static int
make_transform_rule(struct import_rule *rule, const char *expr)
{
	transform_t xform = transform_compile(expr, regex_flags);
	if (!xform) {
		parse_error(_("invalid transform string: %s"),
			    transform_error_string());
		return 1;
	}
	rule->xform = xform;
	rule->type = import_transform;
	return 0;
}

static int
make_regex_rule(struct import_rule *rule)
{
	char *start;
	char *end;
	int rc;
	size_t len;
	char *expr;
	int neg = 0;

	start = rule->literal->text;
	if (start[0] == '!') {
		neg = 1;
		start++;
	} else
		neg = 0;
	end = strchr(start + 1, start[0]);
	start++;
	if (!end)
		return 1;
	len = end - start;
	expr = mu_alloc(len + 1);
	memcpy(expr, start, len);
	expr[len] = 0;
	rc = regcomp(&rule->re, expr, regex_flags);
	if (rc)	{
		char errbuf[512];
		regerror(rc, &rule->re, errbuf, sizeof(errbuf));
		parse_error(_("cannot compile regex (%s): %s"),
			    expr, errbuf);
		free(expr);
		return 1;
	}
	free(expr);
	rule->neg = neg;
	if (end[1] && make_transform_rule (rule, end + 1) == 0)
		return 0;
	rule->type = import_regex;
	return 0;
}

struct import_rule *
import_rule_create(struct literal *lit)
{
	struct import_rule *rule = mu_alloc(sizeof(*rule));
	rule->literal = lit;
	rule->next = NULL;
	if (mu_ispunct(lit->text[0]) && make_regex_rule(rule) == 0)
		return rule;
	rule->type = import_literal;
	return rule;
}

void
import_rules_free(struct import_rule *rules)
{
	while (rules) {
		struct import_rule *next = rules->next;
		switch (rules->type) {
		case import_literal:
			break;
		case import_transform:
			transform_free(rules->xform);
			/* fall through */
		case import_regex:
			regfree(&rules->re);
		}
		rules = next;
	}
}		 

int
import_rules_eval(struct import_rule *rule, const char *name,
		  char **newname)
{
	int res;
	regmatch_t *matches = NULL;
	size_t matchsize = 0;
	size_t matchcount;
	
	if (!rule)
		return 1;
	res = 0;
	*newname = NULL;
	for (; res == 0 && rule; rule = rule->next) {
		switch (rule->type) {
		case import_literal:
			res = strcmp(rule->literal->text, name) == 0;
			break;

		case import_regex:
		case import_transform: /* FIXME */
			matchcount = rule->re.re_nsub + 1;
			if (matchsize < matchcount)
				matches = mu_realloc(matches,
					      sizeof(matches[0]) * matchcount);
			res = regexec(&rule->re, name, matchcount, matches, 0)
				== 0;
			if (rule->neg)
				res = !res;
			if (res && rule->type == import_transform) {
				*newname = transform_string(rule->xform, name);
			}
			break;
		}
	}
	free(matches);
	return res;
}


struct symtab *stab_module;
struct symtab *stab_builtin;
struct symtab *stab_pragma;
struct symtab *stab_literal;
struct variable *builtin_variable_list;

struct symtab *
xsymtab_create(size_t elsize, int flags,
	       void *(*alloc_fun)(size_t), void (*free_fun)(void *))
{
	struct symtab *s = symtab_create(elsize, flags, alloc_fun, free_fun);
	if (!s)
		mu_alloc_die();
	return s;
}

struct syment *
xsymtab_lookup(struct symtab *st, const char *name)
{
	struct syment *ep;
	int rc = symtab_lookup_or_install(&ep, st, name, NULL);
	switch (rc) {
	case ENOMEM:
	case E2BIG:
		mu_error("%s", symtab_strerror(rc));
		abort();
	}
	return rc == 0 ? ep : NULL;
}
	
struct builtin *
xsymtab_install_builtin(const char *name)
{
	struct syment *ep;
	int install = 1;
	int rc = symtab_lookup_or_install(&ep, stab_builtin, name, &install);
	if (rc) {
		parse_error(_("cannot install builtin function %s: %s"),
			    name, symtab_strerror(rc));
		abort();
	}
	return (struct builtin*) ep;
}

void
import_builtin_variables(struct symtab *st)
{
	struct variable *var;

	for (var = builtin_variable_list; var; var = var->next) {
		int install;
		struct syment *ent;
		
		int rc = symtab_lookup_or_install_entry(&ent, st,
							(struct syment *) var,
							&install);
		if (rc == 0 && !install) {
			parse_error("INTERNAL ERROR at %s:%d: "
				    "cannot import builtin variable %s: %s",
				    __FILE__, __LINE__,
				    ((struct syment *) var)->name,
				    "variable already imported");
			abort();
		}
		if (rc) {
			parse_error("INTERNAL ERROR at %s:%d: "
				    "cannot import builtin variable %s: %s",
				    __FILE__, __LINE__,
				    ((struct syment *) var)->name,
				    symtab_strerror(rc));
			abort();
		}
		
	}
}



struct symimport_closure {
	struct module *module;
	struct mu_locus_range locus;
	struct import_rule *rules;
	char *newname;
};

struct mf_symbol *
symbol_resolve_alias(struct mf_symbol *sp)
{
	if (sp) {
		while (sp->alias) 
			sp = (struct mf_symbol *) sp->alias;
	}
	return sp;
}

int
import_selfun(const struct syment *ent, void *closure)
{
	struct symimport_closure *sip = closure;
	struct mf_symbol *sym = (struct mf_symbol*) ent;
	return sym->module == sip->module
		 && (sym->module->flags == 0
		    || (sym->module->flags & SYM_PUBLIC)
		    || ((sym->module->flags & SYM_STATIC)
			&& (sym->flags & SYM_PUBLIC)))
		&& !(sym->flags & SYM_STATIC)
		&& import_rules_eval(sip->rules, ent->name, &sip->newname);
}

int
import_errfun(int rc, struct symtab *tab, const char *name,
	      void *closure)
{
	struct symimport_closure *sip = closure;
	parse_error_locus(&sip->locus,
			  _("cannot import symbol `%s': %s"),
			  name, symtab_strerror(rc));
	return rc;
}

int
import_confun(struct symtab *tab,
	      struct syment *dstent, struct syment *srcent, void *closure)
{
	struct symimport_closure *sip = closure;
	struct mf_symbol *src = (struct mf_symbol*)srcent;
	struct mf_symbol *dst = (struct mf_symbol*)dstent;

	if (mu_locus_point_same_file (&src->locus.beg, &dst->locus.beg))
		return 0;
	parse_error_locus(&sip->locus,
			  _("symbol import conflict: %s"), srcent->name);
	parse_error_locus(&src->locus, _("imported symbol location"));
	parse_error_locus(&dst->locus, _("conflicting symbol location"));
	return EBUSY;
}

int
import_cpyfun(struct syment **pdst, struct syment *src, size_t symsize,
	      void *closure)
{
	struct symimport_closure *sip = closure;
	struct syment *dst;
	
	if (sip->newname) {
		struct mf_symbol *mdst = alloc_entry(sizeof(*mdst));
		struct mf_symbol *s = symbol_resolve_alias((struct mf_symbol*)src);
		mdst->name = mf_strdup(sip->newname);
		mdst->refcnt = 1;
		mdst->alias = (struct mf_symbol*)src;
		mu_locus_range_copy(&mdst->locus, &s->locus);
		dst = (struct syment*)mdst;
		free(sip->newname);
		sip->newname = NULL;
	} else {
		src->refcnt++;
		dst = src;
	}
	*pdst = dst;
	return 0;
}

int
module_import_symbols(struct module *dst, struct module *src,
		      struct mu_locus_range const *loc)
{
	int i;
	struct symimport_closure clos;
	clos.module = src;
	clos.locus = *loc;
	clos.rules = dst->import_rules;
	clos.newname = NULL;
	int res = 0;
	
	for (i = 0; i < MODULE_NAMESPACE_COUNT; i++) {
		if (symtab_import(dst->symtab[i], src->symtab[i],
				  import_selfun, import_errfun,
				  import_confun, import_cpyfun,
				  &clos)) {
			res = 1;
			break;
		}
	}
	import_rules_free(dst->import_rules);
	dst->import_rules = NULL;
	return res;
}


struct module *top_module;
struct module_list *module_stack;
struct module_list *module_head, *module_tail;

static void
module_register(struct module *mod)
{
	struct module_list *p = alloc_entry(sizeof(*p));
	p->module = mod;
	p->next = NULL;
	if (module_tail)
		module_tail->next = p;
	else
		module_head = p;
	module_tail = p;
}

int
module_symtab_enumerate(enum module_namespace ns, symtab_enumerator_t fun,
			void *data)
{
	struct module_list *p;

	for (p = module_head; p; p = p->next) {
		int rc = symtab_enumerate(p->module->symtab[ns], fun, data);
		if (rc)
			return rc;
	}
	return 0;
}
	
static void
module_init(struct module *mod, const char *name, const char *file)
{
	mod->name = mf_strdup(name);
	mod->file = file ? mf_strdup(file) : NULL;
	mod->dclname = NULL;
	mod->flags = 0;
	mod->symtab[namespace_function] =
		xsymtab_create(sizeof(struct function), SYMTAB_COPY_KEY,
			       alloc_entry, free_entry);
	mod->symtab[namespace_variable] =
		xsymtab_create(sizeof(struct variable), SYMTAB_COPY_KEY,
			       alloc_entry, free_entry);
	mod->symtab[namespace_constant] =
		xsymtab_create(sizeof(struct constant), SYMTAB_COPY_KEY,
			       alloc_entry, free_entry);
	mod->import_rules = NULL;
	mod->submodule_head = mod->submodule_tail = NULL;
	mod->incl_sources = NULL;

	module_register(mod);
}

static struct module *
module_create(const char *name, const char *file)
{
	struct module *mod = alloc_entry(sizeof(*mod));
	module_init(mod, name, file);
	return mod;
}

static void
module_free(struct module *mod)
{
	int i;
	if (!mod)
		return;
	for (i = 0; i < MODULE_NAMESPACE_COUNT; i++)
		symtab_destroy(&mod->symtab[i]);
	mu_list_destroy(&mod->incl_sources);
}	

static void
module_add_submodule(struct module *mod, struct module *submod)
{
	struct module_list *p = alloc_entry(sizeof(*p));
	p->module = submod;
	p->next = NULL;
	if (mod->submodule_tail)
		mod->submodule_tail->next = p;
	else
		mod->submodule_head = p;
	mod->submodule_tail = p;
}

static int
module_has_submodule(struct module *mod, struct module *submod)
{
	struct module_list *p;
	for (p = mod->submodule_head; p; p = p->next)
		if (p->module == submod)
			return 1;
	return 0;
}

static void
module_push(struct module *mod)
{
	struct module_list *p = alloc_entry(sizeof(*p));
	p->module = mod;
	p->next = module_stack;
	module_stack = p;
}

static struct module *
module_pop()
{
	struct module_list *p = module_stack;
	struct module *mod;
	if (p) {
		mod = p->module;
		module_stack = p->next;
	} else
		mod = NULL;
	return mod;
}

int
set_top_module(const char *name, const char *file,
	       struct import_rule *import_rules,
	       struct mu_locus_range const *locus)
{
	struct module *mod;
	struct syment *ent;
	int install = 1;
	int rc = symtab_lookup_or_install(&ent, stab_module,
					  name, &install);
	if (rc) {
		parse_error_locus(locus,
				  _("cannot install module %s: %s"),
				  name, symtab_strerror(rc));
		abort();
	}
	mod = (struct module *) ent;
	if (!install) {
		if (!module_has_submodule(top_module, mod))
			/* Import symbols from mod to top_module */
			module_import_symbols(top_module, mod, locus);
		return 1;
	}
	module_init(mod, name, file);
	import_builtin_variables(mod->symtab[namespace_variable]);
	top_module->import_rules = import_rules;
	module_add_submodule(top_module, mod);
	module_push(top_module);
	top_module = mod;
	return 0;
}

void
pop_top_module()
{
	if (module_stack) {
		struct module *mod = top_module;
		top_module = module_pop();
		/* Import symbols from mod to top_module */
		if (mod != top_module) {
			struct mu_locus_range lr = MU_LOCUS_RANGE_INITIALIZER;
			module_import_symbols(top_module, mod, &lr); /*FIXME: locus?*/
		}
	}
}

static int
_collect_modules(void *ep, void *data)
{
	struct module ***pmod = data;
	*(*pmod)++ = ep;
	return 0;
}

static int
_module_comp(const void *a, const void *b)
{
	const struct module *am = a;
	const struct module *bm = b;
	return strcmp(am->name, bm->name);
}

void
collect_modules(struct module ***pmodv, size_t *pmodc)
{
	size_t modc = symtab_count_entries(stab_module);
	struct module **modv = mu_calloc(modc + 1, sizeof(*modv));
	struct module **tmp = modv + 1;

	modv[0] = top_module;
	symtab_enumerate(stab_module, _collect_modules, &tmp);
	qsort(modv + 1, modc, sizeof(modv[0]), _module_comp);
	modc++;
	*pmodv = modv;
	*pmodc = modc;
}


static void
free_module_entry(void *ptr)
{
	module_free((struct module *)ptr);
}

void
init_symbols()
{
	stab_module = xsymtab_create(sizeof(struct module),
				     SYMTAB_COPY_KEY,
				     alloc_entry, free_module_entry);
	stab_builtin = xsymtab_create(sizeof(struct builtin),
				      SYMTAB_COPY_KEY,
				      alloc_entry, free_entry);
	stab_pragma = xsymtab_create(sizeof(struct pragma),
				     SYMTAB_COPY_KEY,
				     alloc_entry, free_entry);
	stab_literal = xsymtab_create(sizeof(struct literal),
				      SYMTAB_COPY_KEY,
				      alloc_entry, free_entry);
	top_module = module_create("top", script_file);
}

void
free_symbols()
{
	symtab_destroy(&stab_module);
	symtab_destroy(&stab_builtin);
	symtab_destroy(&stab_pragma);
	symtab_destroy(&stab_literal);

	module_free(top_module);
	top_module = NULL;
	free_buckets();
}



void
va_builtin_install(char *name,
		   void (*handler) (eval_environ_t),
		   data_type_t rettype,
		   size_t argcount,
		   ...)
{
	struct builtin *builtin;
	va_list ap;
	size_t i;
	data_type_t *argtype = mu_alloc (argcount * sizeof *argtype);
	
	va_start(ap, argcount);
	for (i = 0; i < argcount; i++) 
		argtype[i] = va_arg(ap, data_type_t);
	va_end(ap);

	builtin = xsymtab_install_builtin(name);

	/* new entry */
	builtin->name = mf_strdup(name);
	builtin->handler = handler;
	builtin->parmcount = argcount;
	builtin->parmtype = argtype;
	builtin->rettype = rettype;
	builtin->statemask = 0;
	builtin->flags = 0;
}

void
va_builtin_install_ex (char *name,
		       void (*handler) (eval_environ_t),
		       unsigned statemsk,
		       data_type_t rettype,
		       size_t argcount,
		       size_t optcount,
		       int flags,
		       ...)
{
	struct builtin *builtin;
	va_list ap;
	size_t i;
	data_type_t *argtype = mu_alloc (argcount * sizeof *argtype);

 	va_start(ap, flags);
	for (i = 0; i < argcount; i++) 
		argtype[i] = va_arg(ap, data_type_t);
	va_end(ap);

	builtin = xsymtab_install_builtin(name);

	/* new entry */
	builtin->name = mf_strdup(name);
	builtin->handler = handler;
	builtin->parmcount = argcount;
	builtin->optcount = optcount;
	builtin->parmtype = argtype;
	builtin->rettype = rettype;
	builtin->statemask = statemsk;
	builtin->flags = flags;
}

const struct builtin *
builtin_lookup(const char *name)
{
	return (struct builtin *)xsymtab_lookup(stab_builtin, name);
}



static void
init_variable(struct variable *var)
{
	mu_locus_range_init (&var->sym.locus);
	var->sym.module = top_module;
	var->sym.flags = 0;
	var->type = dtype_unspecified;
	var->storage_class = storage_extern;
	var->off = 0;
	var->addrptr = NULL;
	var->shadowed = NULL;
	var->initialized = 0;
	var->xref = NULL;
	var->next = NULL;
}	

struct variable *
variable_install(const char *name)
{
	struct variable *vp;
	struct syment *ent;
	int install = 1;
	int rc = symtab_lookup_or_install(&ent,
					 TOP_MODULE_SYMTAB(namespace_variable),
					  name, &install);
	if (rc) {
		parse_error(_("cannot install variable %s: %s"),
			    name, symtab_strerror(rc));
		abort();
	}

	vp = (struct variable *) ent;
	if (install) {
		init_variable(vp);
		vp->sym.name = mf_strdup(name);
	}
	return vp;
}

struct variable *
variable_replace(const char *name, struct variable *var)
{
	if (!var) {
		var = alloc_entry(sizeof(*var));
		init_variable(var);
		var->sym.name = mf_strdup(name);
	}
	if (symtab_replace(TOP_MODULE_SYMTAB(namespace_variable),
			   (struct syment*)var, NULL)) {
		parse_error(_("INTERNAL ERROR: variable %s not found"),
			    name);
		abort();
	}
	return var;
}

struct variable *
variable_lookup(const char *name)
{
	struct variable *var = (struct variable *)
		xsymtab_lookup(TOP_MODULE_SYMTAB(namespace_variable), name);
	return (struct variable *)symbol_resolve_alias((struct mf_symbol*)var);
}

struct variable *
builtin_variable_install(const char *name, data_type_t type, unsigned flags,
			 size_t *addrptr)
{
	struct variable *var = variable_install(name);
	var->sym.flags = flags;
	var->sym.module = NULL;
	var->type = type;
	var->addrptr = addrptr;
	var->next = builtin_variable_list;
	builtin_variable_list = var;
	return var;
}


static struct function *
function_lookup_or_install(const char *name, struct mu_locus_range const *locus)
{
	struct function *fp;
	struct syment *ent;
	int install = 1;
	int rc = symtab_lookup_or_install(&ent,
					 TOP_MODULE_SYMTAB(namespace_function),
					  name, &install);
	if (rc) {
		parse_error(_("cannot install function %s: %s"),
			    name, symtab_strerror(rc));
		abort();
	}

	fp = (struct function*) ent;
	if (!install) {
		if (fp->sym.alias) {
			fp = (struct function *)
				symbol_resolve_alias((struct mf_symbol*)fp);
			parse_error_locus(locus,
				  _("redefinition of function %s by alias %s"),
					  fp->sym.name, name);
			parse_error_locus(&fp->sym.locus, 
		                  _("previously defined here"));
		} else {
			parse_error_locus(locus,
					  _("redefinition of function %s"),
					  name);
			parse_error_locus(&fp->sym.locus, 
					  _("previously defined here"));
		}
	}
	return fp;
}

struct function *
function_install(const char *name, size_t parmcnt, size_t optcnt, int varargs,
		 data_type_t vartype, data_type_t *parmtypes, data_type_t rettype,
		 struct mu_locus_range const *locus)
{
	struct function *fp = function_lookup_or_install(name, locus);
	fp->sym.name = mf_strdup(name);
	fp->sym.module = top_module;
	mu_locus_range_copy(&fp->sym.locus, locus);
	fp->node = NULL;
	fp->entry = 0;
	fp->parmcount = parmcnt;
	fp->optcount = optcnt;
	fp->varargs = varargs;
	fp->vartype = vartype;
	fp->parmtype = parmtypes;
	fp->rettype = rettype;
	fp->exmask = exmask_create();
	fp->statemask = 0;
	return fp;
}

struct function *
function_lookup(const char *name)
{
	struct function *fp = (struct function *)
		xsymtab_lookup(TOP_MODULE_SYMTAB(namespace_function), name);
	return (struct function*) symbol_resolve_alias((struct mf_symbol*)fp);
}


void
install_alias(const char *name, struct function *fun,
	      struct mu_locus_range const *locus)
{
	struct function *fp = function_lookup_or_install(name, locus);
	fp->sym.name = mf_strdup(name);
	fp->sym.module = top_module;
	mu_locus_range_copy(&fp->sym.locus, locus);
	fp->sym.alias = (struct mf_symbol*) fun;
}


struct literal *
literal_lookup(const char *text)
{
	struct literal *lit;
	struct syment *ent;
	int install = 1;
	int rc = symtab_lookup_or_install(&ent, stab_literal, text, &install);
	if (rc) {
		parse_error(_("cannot install literal %s: %s"),
			    text, symtab_strerror(rc));
		abort();
	}
	
	lit = (struct literal *)ent;
	if (install) {
		lit->flags = 0;
		lit->regex = NULL;
	}
	return lit;
}

struct constant *
define_constant(const char *name, struct value *value, unsigned flags,
		struct mu_locus_range const *locus)
{
	struct constant *cp;
	struct syment *ent;
	int install = 1;
	int rc = symtab_lookup_or_install(&ent,
					 TOP_MODULE_SYMTAB(namespace_constant),
					  name, &install);
	if (rc) {
		parse_error(_("cannot install constant %s: %s"),
			    name, symtab_strerror(rc));
		abort();
	}

	cp = (struct constant *) ent;
	if (install) {
		cp->sym.name = mf_strdup(name);
	} else {
		parse_warning_locus(locus,
				    "%s: redefinition of constant",
				    name);
		parse_warning_locus(&cp->sym.locus,
				    "this is the location of the "
				    "previous definition");
	}
	cp->sym.module = top_module;
	mu_locus_range_copy(&cp->sym.locus, locus);
	cp->sym.flags = flags;
	cp->value = *value;
	return cp;
}

const struct constant *
constant_lookup(const char *name)
{
	const struct constant *cp = (const struct constant *)
		xsymtab_lookup(TOP_MODULE_SYMTAB(namespace_constant), name);
	return (const struct constant *)
		symbol_resolve_alias((struct mf_symbol*)cp);
}

const struct value *
constant_lookup_value(const char *name)
{
	const struct constant *cptr = constant_lookup(name);
	return cptr ? &cptr->value : NULL;
}


struct sym_regex *
install_regex(struct literal *lit, unsigned regflags)
{
	struct sym_regex *regex;

	for (regex = lit->regex; regex; regex = regex->next)
		if (regex->regflags == regflags)
			return regex;

	/* FIXME: don't use alloc_entry here ...
	   or change sym_regex to match struct syment structure */
	regex = alloc_entry(sizeof(*regex));
	regex->lit = lit;
	regex->regflags = regflags;
	regex->flags = 0;
	regex->index = 0;
	regex->next = lit->regex;
	lit->regex = regex;
	
	return regex;
}


void
install_pragma(const char *name, int minargs, int maxargs,
	       void (*handler) (int, char **, const char *))
{
	struct pragma *pragma;
	struct syment *ent;
	int install = 1;
	int rc = symtab_lookup_or_install(&ent, stab_pragma, name, &install);
	if (rc) {
		parse_error(_("cannot install pragma %s: %s"),
			    name, symtab_strerror(rc));
		abort();
	}
	if (!install) {
		parse_error(_("INTERNAL ERROR: pragma %s already defined"),
			    name);
		abort();
	}

	pragma = (struct pragma *)ent;
	pragma->name = mf_strdup(name);
	pragma->minargs = minargs;
	pragma->maxargs = maxargs;
	pragma->handler = handler;
}

const struct pragma *
lookup_pragma(const char *name)
{
	return (const struct pragma *)xsymtab_lookup(stab_pragma, name);
}
