/* @(#)checkerr.c	1.8 05/03/25 Copyright 2003 J. Schilling */
#ifndef lint
static	char sccsid[] =
	"@(#)checkerr.c	1.8 05/03/25 Copyright 2003 J. Schilling";
#endif
/*
 *	Error control for star.
 *
 *	Copyright (c) 2003 J. Schilling
 */
/*
 * Copyright Jörg Schilling. All rights reserved.
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only.
 * See the file CDDL.Schily.txt in this distribution or
 * http://opensource.org/licenses/cddl1.php for details.
 */

#include <mconfig.h>
#include <stdio.h>
#include <standard.h>
#include <patmatch.h>
#include <strdefs.h>
#include <utypes.h>
#include <schily.h>

#include "starsubs.h"
#include "checkerr.h"

typedef struct errconf {
	struct errconf	*ec_next;	/* Next in list			    */
	const	Uchar	*ec_pat;	/* File name pattern		    */
	int		*ec_aux;	/* Aux array from pattern compiler  */
	int		ec_alt;		/* Alt return from pattern compiler */
	int		ec_plen;	/* String length of pattern	    */
	int		ec_flags;	/* Error condition flags	    */
} ec_t;

LOCAL	int	*ec_state;		/* State array for pattern compiler */
LOCAL	ec_t	*ec_root;		/* Root node of error config list   */

EXPORT	int	errconfig	__PR((char *name));
LOCAL	int	errflags	__PR((char *eflag));
EXPORT	BOOL	errhidden	__PR((int etype, char *fname));

/*
 * Read and parse error configuration file
 */
EXPORT int
errconfig(name)
	char	*name;
{
	char	line[8192];
	FILE	*f;
	int	plen;
	int	maxplen = 0;
	char	*flags;
	char	*pattern;
	ec_t	*ep;
	ec_t	**epp = &ec_root;

	if ((f = fileopen(name, "r")) == NULL)
		comerr("Cannot open '%s'.\n", name);

	while (fgetline(f, line, sizeof (line)) >= 0) {

		flags = line;
		/*
		 * Find end of word.
		 */
		for (pattern = line;
				*pattern != '\0' &&
				*pattern != '\t' &&
				*pattern != ' ';
					pattern++) {
			;
			/* LINTED */
		}
		if (pattern == line || *pattern == '\0') {
			comerrno(EX_BAD,
			"Bad error configuration line '%s'.\n", line);
		}
		/*
		 * Find end of white space after word.
		 */
		if (*pattern != '\0')
			*pattern++ = '\0';
		for (; *pattern != '\0' &&
		    (*pattern == '\t' || *pattern == ' ');
					pattern++) {
			;
			/* LINTED */
		}
		ep = __malloc(sizeof (ec_t), "errcheck node");
		ep->ec_flags = errflags(flags);
		ep->ec_plen = plen = strlen(pattern);
		if (ep->ec_plen > maxplen)
			maxplen = ep->ec_plen;
		ep->ec_pat = (const Uchar *)__savestr(pattern);
		ep->ec_aux = __malloc(plen*sizeof (int), "compiled pattern");
		if ((ep->ec_alt = patcompile((const Uchar *)pattern,
						plen, ep->ec_aux)) == 0)
			comerrno(EX_BAD, "Bad pattern: '%s'.\n", pattern);

		ep->ec_next = NULL;
		*epp = ep;
		epp = &ep->ec_next;
	}
	fclose(f);
	ec_state = __malloc((maxplen+1)*sizeof (int), "pattern state");
	return (0);
}

LOCAL struct eflags {
	char	*fname;
	int	fval;
} eflags[] = {
	{ "STAT",		E_STAT },
	{ "GETACL",		E_GETACL },
	{ "OPEN",		E_OPEN },
	{ "READ",		E_READ },
	{ "WRITE",		E_WRITE },
	{ "GROW",		E_GROW },
	{ "SHRINK",		E_SHRINK },
	{ "MISSLINK",		E_MISSLINK },
	{ "NAMETOOLONG",	E_NAMETOOLONG },
	{ "FILETOOBIG",		E_FILETOOBIG },
	{ "SPECIALFILE",	E_SPECIALFILE },
	{ "READLINK",		E_READLINK },
	{ "GETXATTR",		E_GETXATTR },

	{ "SETTIME",		E_SETTIME },
	{ "SETMODE",		E_SETMODE },
	{ "SECURITY",		E_SECURITY },
	{ "LSECURITY",		E_LSECURITY },
	{ "SAMEFILE",		E_SAMEFILE },
	{ "BADACL",		E_BADACL },
	{ "SETACL",		E_SETACL },
	{ "SETXATTR",		E_SETXATTR },

	{ NULL,			0 }
};

/*
 * Convert error condition string into flag word
 */
LOCAL int
errflags(eflag)
	char	*eflag;
{
	register char		*p = eflag;
	register struct eflags	*ep;
	register int		slen;
	register int		nflags = 0;

	do {
		for (ep = eflags; ep->fname; ep++) {
			slen = strlen(ep->fname);
			if ((strncmp(ep->fname, p, slen) == 0) &&
			    (p[slen] == '|' || p[slen] == ' ' ||
			    p[slen] == '\0')) {
				nflags |= ep->fval;
				break;
			}
		}
		if (ep->fname == NULL)
			comerrno(EX_BAD, "Bad flag '%s'\n", p);
		p = strchr(p, '|');
	} while (p && *p++ == '|');

	return (nflags);
}

/*
 * Check whether error condition should be ignored for file name.
 */
EXPORT BOOL
errhidden(etype, fname)
	int	etype;
	char	*fname;
{
	ec_t		*ep = ec_root;
	char		*ret;
	const Uchar	*name = (const Uchar *)fname;
	int		nlen;

	if (fname == NULL) {
		errmsgno(EX_BAD,
			"Implementation botch for errhidden(0x%X, NULL)\n",
			etype);
		errmsgno(EX_BAD, "Please report this bug!\n");
		errmsgno(EX_BAD, "Error cannot be ignored.\n");
		return (FALSE);
	}
	nlen  = strlen(fname);
	while (ep) {
		if ((ep->ec_flags & etype) != 0) {
			ret = (char *)patmatch(ep->ec_pat, ep->ec_aux,
					name, 0,
					nlen, ep->ec_alt, ec_state);
			if (ret != NULL && *ret == '\0')
				return (TRUE);
		}
		ep = ep->ec_next;
	}
	return (FALSE);
}
