/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)libadm:getdev.c	1.4.6.1"
/*LINTLIBRARY*/

/*
 *  getdev.c
 *
 *  Contents:
 *	getdev()	List devices that match certain criteria.
 */

/*
 * Header files referenced:
 *	<sys/types.h>	System Data Types
 *	<errno.h>	Error handling
 *	<fcntl.h>	File controlling
 *	<ctype.h>	Character types
 *	<string.h>	String handling
 *	<devmgmt.h>	Global device-management def'ns
 *	"devtab.h"	Local device-management dev'ns
 */

#include	<sys/types.h>
#include	<errno.h>
#include	<fcntl.h>
#include	<ctype.h>
#include	<string.h>
#include	<devmgmt.h>
#include	"devtab.h"

/*
 * Local definitions
 *	NULL		Nil address
 *	TRUE		Boolean TRUE
 *	FALSE		Boolean FALSE
 */

#ifndef	NULL
#define	NULL			0
#endif

#ifndef	TRUE
#define	TRUE			('t')
#endif

#ifndef	FALSE
#define	FALSE			0
#endif


/*
 *  Comparison values.  These values are placed in the struct srch
 *  structure by buildsearchlist() and are used to compare values
 *  in matches().
 *	EQUAL		Attribute must equal this value
 *	NOTEQUAL	Attribute must not equal this value
 *	EXISTS		Attribute must exist
 *	NOEXISTS	Attribute must not exist
 *	IGNORE		Ignore this entry
 *	ENDLIST		This entry ends the list
 */

#define	EQUAL			1
#define	NOTEQUAL		2
#define	EXISTS			3
#define	NOEXISTS		4
#define	IGNORE			5
#define	ENDLIST			0


/*
 *  Structure definitions:
 * 	deviceent	Defines a device that matches criteria
 *	srch		Describes a criteria
 */

struct deviceent {
	struct deviceent       *next;	/* Pointer to next item in the list */
	char		       *name;	/* Presentation name of the device */
};

struct srch {
	char   *name;			/* Name of field to compare */
	char   *cmp;			/* Value to compare against */
	int	fcn;			/* Type of comparison (see above) */
};

/*
 * Externals referenced
 *	malloc()	Allocate a chunk of main memory
 *	free()		Free malloc()ed memory
 *	realloc()	Shrink or enlarge malloc()ed memory
 *	devattr()	Get a device's attributes
 */

extern	void	       *malloc();
extern	void		free();
extern	void	       *realloc();
extern	char	       *devattr();


/*
 * Local functions referenced
 *	oktoaddtolist()		Determines if device can be added to the
 *				list by examining the devices list and
 *				the options governing the search
 *	initdevicelist()	Initializes the linked list of devices
 *				to be included in the list-to-return
 *	freedevicelist()	Frees the resources allocated to the linked
 *				list of devices
 *	addtodevicelist()	Adds an entry to the linked list of devices
 *	buildsearchlist()	Builds a list of struct srch structures from
 *				the criteria strings
 *	freesearchlist()	Frees the resources allocated to the list of
 *				struct srch structures
 *	buildreturnlist()	Builds the list of devices to return from the
 *				linked list of devices we've accumulated
 *	makealiaslist()		Builds a list of aliases from the list of
 *				devices presented by the caller
 *	freealiaslist()		Frees the resources allocated to the list of
 *				devices aliases
 *	getnextmatch()		Get the next device that matches the search
 *				criteria
 *	matchallcriteria()	See if the device attributes match all of the
 *				search criteria
 *	matchanycriteria()	See if the device attributes match any of the
 *				search criteria
 *	matches()		See if the criteria and attribute match
 */

static	char	       *oktoaddtolist();
static	void		initdevicelist();
static	void		freedevicelist();
static	int		addtodevicelist();
static	struct srch    *buildsearchlist();
static	void 		freesearchlist();
static	char	      **buildreturnlist();
static	char	      **makealiaslist();
static	void		freealiaslist();
static	char	       *getnextmatch();
static	int		matchallcriteria();
static	int		matchanycriteria();
static	int		matches();


/*
 * Global Data
 */

/*
 * Static Data
 *	devicelisthead	The first item (dummy) in the linked list of devices
 *			we're building
 *	devicelist	Structure describing the linked list of devices
 */

static	struct deviceent	devicelisthead;
static	struct {
	struct deviceent       *head;
	int			count;
} devicelist = {&devicelisthead, 0};

/*
 *  char **getdev(devices, criteria, options)
 *	char  **devices
 *	char  **criteria
 *	int	options
 *
 *	This function builds a list of devices that match criteria,
 *	governed by the device list.
 *
 *  Arguments:
 *	devices		The list of devices to select from or the list of
 *			devices to exclude, depending on the value of
 *			"options"
 *	criteria	The list of criteria governing the device selection
 *			Of the form <attr><op><val>
 *	options		Options controlling the device selection.  May require
 *			that a device meet all of the criteria (default is
 *			any one of the criteria), or may require that the
 *			devices in the list of devices be excluded from the
 *			generated list (default is to select only those
 * 			devices in the list)
 *
 *  Returns:  char **
 *	The address of the first item in the list of devices that meet
 *	the selection criteria
 */

char  **
getdev(devices, criteria, options)
	char  **devices;		/* List of devices to constrain */
	char  **criteria;		/* List of selection criteria */
	int	options;		/* Options governing the search */
{
	/* Automatic data */
	char	      **aliases;	/* List of constraining devices */
	char	      **returnlist;	/* List of ptrs to aliases to return */
	struct srch    *searchlist;	/* Pointer to searching criteria */
	char	       *entry;		/* Pointer to alias in record */
	int		errflag;	/* FLAG:  TRUE if error */


	/*
	 *  Initializations
	 */

	/*  Make sure the exclude/include list is all aliases */
	aliases = makealiaslist(devices);
	if (devices && !aliases) return((char **) NULL);

	/*  Build the search list  */
	if (criteria) {
	    if (!(searchlist = buildsearchlist(criteria, options)))
		return((char **) NULL);
	} else searchlist = (struct srch *) NULL;

	/*  Initialize searching  */
	initdevicelist();
	_setdevtab();


	/*
	 *  Keep on going until we get no more matches
	 */

	errflag = FALSE;
	while (!errflag && (entry = getnextmatch(searchlist, options))) {
	    if (entry = oktoaddtolist(entry, devices, aliases, options)) {
		errflag = addtodevicelist(entry);
	    }
	}


	/*
	 *  Clean up:
	 *    -	Free the entry space we've allocated.
	 *    -	Close the device table.
	 *    - Build the list to return to the caller.
	 *    - Free the accumulate device space (but not the strings!)
	 *    - Free the alias list
	 *    - Return the built list to the caller.
	 */

	returnlist = buildreturnlist();
	freedevicelist();
	freealiaslist(aliases);
	_enddevtab();
	return(returnlist);
}

/*
 *  char *oktoaddtolist(devtabentry, devices, aliases, options)
 *	char   *devtabentry
 *	char  **devices
 *	char  **aliases
 *	int	options
 *
 *	This function determines the device "devtabentry" can be
 *	added to the list of devices we're accumulating.  If so,
 *	it returns the device name (not the alias).
 *
 *  Arguments:
 *	devtabentry	The device alias that may or may not belong in the
 *			list we're building.
 *	devices		The devices specified by the caller
 *	aliases		The aliases of the devices specified by the caller
 *			(1-1 correspondence with "devices")
 *	options		Options controlling the search
 */

static	char *
oktoaddtolist(devtabentry, devices, aliases, options)
	char   *devtabentry;	/* Alias to check against list */
	char  **devices;	/* List of devices to check against */
	char  **aliases;	/* List of alias of those devices */
	int	options;	/* Options governing search */
{
	/* Automatic data */
	char   *rtnval;		/* Value to return */
	int	found;		/* Flag:  TRUE if found */

	/* If there's a constraint list, is this device in it? */
	if (devices && aliases) {

	    /* Set "found" to TRUE if the device is in the list */
	    found = FALSE;
	    while (!found && *aliases) {
		if (strcmp(devtabentry, *aliases) == 0) found = TRUE;
		else {
		    devices++;
		    aliases++;
		}
	    }

	    /* Set value to return */
	    if (found) rtnval = (options&DTAB_EXCLUDEFLAG) ? (char *) NULL : *devices;
	    else rtnval = (options&DTAB_EXCLUDEFLAG) ? devtabentry : (char *) NULL;

	} else rtnval = devtabentry;  /* No constraint list */

	return(rtnval);
}

/*
 *  void initdevicelist()
 *
 *	This function initializes the list of accumulated devices.
 *
 *  Arguments:  None
 *
 *  Returns:  Void.
 *
 *  Notes:
 */

static	void
initdevicelist()
{
	/* Make the list a null list */
	(devicelist.head)->next = (struct deviceent *) NULL;
	devicelist.count = 0;
}

/*
 *  void freedevicelist()
 *
 *	This function frees the resources allocated to the linked list of
 *	devices we've been accumulating.
 *
 *  Arguments:  none
 *
 *  Returns:  void
 */

static	void
freedevicelist()
{
	/* Automatic data */
	struct deviceent       *pdevice;	/* Pointer to current entry */
	char		       *freeblk;	/* Pointer space to free */

	/* List has a dummy head node */
	pdevice = (devicelist.head)->next;
	while (pdevice) {
	    freeblk = (char *) pdevice;
	    pdevice = pdevice->next;
	    free(freeblk);
	}
}

/*
 *  int addtodevicelist(deventry)
 *	char   *deventry
 *
 * 	This function adds the device <deventry> to the list of devices already
 *	accumulated.  It will not add the device if that device already exists
 *	in the list.  The function returns 0 if successful, -1 if not with
 *	"errno" set (by functions called) to indicate the error.
 *
 *  Arguments:
 *	deventry		char *
 *				The name of the device to add to the list of
 *				accumulated devices
 *
 *  Returns:
 *	0	If successful
 *	-1	If failed.  "errno" will be set to a value that indicates the
 *		error.
 *
 *  Notes:
 *    -	The memory allocation scheme has the potential to fragment the memory
 *	in the malloc heap.  We're allocating space for a local structure,
 *	which will be freed by getdev(), then allocating space for the device
 *	name, which will be freed (maybe) by the application using getdev().
 *	Not worrying about this at the moment.
 */

static	int
addtodevicelist(deventry)
	char   *deventry;
{
	/* Automatic data */
	struct deviceent       *p;	/* Pointer to current device */
	struct deviceent       *q;	/* Pointer to next device */
	struct deviceent       *new;	/* Pointer to the alloc'd new node */
	char		       *str;	/* Pointer to alloc'd space for name */
	int			rtncd;	/* Value to return to the caller */
	int			cmpcd;	/* strcmp() value, comparing names */
	int			done;	/* Loop control, TRUE if done */


	/* Initializations */
	rtncd = FALSE;


	/*
	 * Find the place in the found device list devicelist where this
	 * device is to reside
	 */

	p = devicelist.head;
	done = FALSE;
	while (!done) {
	    if (!(q = p->next)) done = TRUE;
	    else if ((cmpcd = strcmp(deventry, q->name)) <= 0) done = TRUE;
	    else p = q;
	}

	/*
	 *  If the device is not already in the list, insert it in the list
	 */

	if (!q || (cmpcd != 0)) {

	    /* Alloc space for the new node */
	    if (new = (struct deviceent *) malloc(sizeof(struct deviceent))) {

		/* Alloc space for the device character string */
		if (str = (char *) malloc((unsigned) strlen(deventry)+1)) {

		    /*
		     * Insert an entry in the found device list containing
		     * this device name
		     */
		    new->next = q;
		    p->next = new;
		    new->name = strcpy(str, deventry);
		    devicelist.count++;
		}

		/* Couldn't alloc space for the device name.  Error. */
		else rtncd = TRUE;
	    }

	    /* Couldn't alloc space for new node in the found list.  Error. */
	    else rtncd = TRUE;

	}

	/* Return an value indicating success or failure */
	return(rtncd);
}

/*
 *  struct srch *buildsearchlist(criteria)
 *	char  **criteria
 *
 *	This function builds a list of search criteria structures from the
 *	criteria strings in the list of criteria whose first argument is
 *	specified by "criteria".
 *
 *  Arguments:
 *	criteria	The address of the first item in a list of
 *			character-strings specifying search criteria
 *
 *  Returns: struct srch *
 *	The address of the structure in the list of structures describing the
 *	search criteria.
 *
 *  Notes:
 *    -	The <options> argument isn't currently being used.  It is still
 *	among the arguments to this function for historical (hysterical?)
 *	reasons.  Since someday the options may affect this function,
 *	the argument is being kept around.
 *    -	The only "regular expression" currently supported by the
 *	kywd:exp and kywd!:exp forms is exp=*.  This function assumes
 *	that kywd:exp means "if kywd exist" and that kywd!:exp means
 *	"if kywd doesn't exist".
 */

static 	struct srch *
buildsearchlist(criteria)
	char	      **criteria;	/* Criteria from caller */
{
	/*  Automatic data  */
	struct srch    *rtnbuf;		/* Value to return */
	struct srch    *psrch;		/* Running pointer */
	char	       *str;		/* Ptr to malloc()ed string space */
	char	       *p;		/* Temp pointer to char */
	int		noerror;	/* TRUE if all's well */
	int		n;		/* Temp counter */
	char	      **pp;		/* Running ptr to (char *) */


	/*  Initializations  */
	rtnbuf = (struct srch *) NULL;		/* Nothing to return yet */
	noerror = TRUE;				/* No errors (yet) */

	/* If we were given any criteria ... */
	if (criteria) {

	    /* Count the number of criteria in the list */
	    for (n = 1, pp = criteria ; *pp++ ; n++) ;

	    /* Allocate space for structures describing the criteria */
	    if (rtnbuf = (struct srch *) malloc(n*sizeof(struct srch))) {

		/* Build structures describing the criteria */
		pp = criteria;
		psrch = rtnbuf;
		while (noerror && *pp) {

		    /* Keep list sane for cleanup if necessary */
		    psrch->fcn = ENDLIST;

		    /* Alloc space for strings referenced by the structure */
		    if (str = (char *) malloc((unsigned) strlen(*pp)+1)) {

			/* Extract field name, function, and compare string */
			(void) strcpy(str, *pp);

			/* If criteria contains an equal sign ('=') ... */
			if (p = strchr(str+1, '=')) {
			    if (*(p-1) == '!') {
				*(p-1) = '\0';
				psrch->fcn = NOTEQUAL;
			    }
			    else {
				*p = '\0';
				psrch->fcn = EQUAL;
			    }
			    psrch->cmp = p+1;
			    psrch->name = str;
			    psrch++;
			}

			/* If criteria contains a colon (':') ... */
			else if (p = strchr(str+1, ':')) {
			    if (*(p-1) == '!') {
				*(p-1) = '\0';
				psrch->fcn = NOEXISTS;
			    }
			    else {
				*p = '\0';
				psrch->fcn = EXISTS;
			    }
			    psrch->cmp = p+1;
			    psrch->name = str;
			    psrch++;
			}
		    }
		    else {
			/* Unable to malloc() string space.  Clean up */
			freesearchlist(rtnbuf);
			noerror = FALSE;
		    }
		    /* Next criteria */
		    pp++;
		}
		/* Terminate list */
		if (noerror) psrch->fcn = ENDLIST;
	    }
	    else {
		/* Unable to malloc() list space */
		noerror = FALSE;
	    }
	}

	/* Return a pointer to allocated space (if any) */
	return(rtnbuf);
}

/*
 *  void freesearchlist(list)
 *	struct srch  *list
 *
 *	This function frees the resources allocated to the searchlist <list>.
 *
 *  Arguments:
 *	list		The list whose resources are to be released.
 *
 *  Returns:  void
 */

static	void
freesearchlist(list)
	struct srch	       *list;
{
	/* Automatic data */
	struct srch	       *psrch;		/* Running ptr to structs */


	/* Free all of the string space allocated for the structure elememts */
	for (psrch = list ; psrch->fcn != ENDLIST ; psrch++) {
	    free(psrch->name);
	}

	/* Free the list space */
	free(list);
}

/*
 *  char **buildreturnlist()
 *
 *	This function builds a list of addresses of character-strings
 *	to be returned from the linked-list of devices we've been
 *	building.  It returns a pointer to the first item in that list.
 *
 *  Arguments:  none
 *
 *  Returns:  char **
 *	The address of the first item in the return list
 */

static	char **
buildreturnlist()
{
	/* Automatic data */
	char		      **list;
	char		      **q;
	struct deviceent       *p;


	/*
	 * Allocate space for the return list,
	 * with space for the terminating node
	 */

	if (list = (char **) malloc((devicelist.count+1)*sizeof(char *))) {

	    /*
	     * Walk the list of accumulated devices, putting pointers to
	     * device names in the list to return
	     */

	    q = list;
	    for (p = devicelist.head->next ; p ; p = p->next) *q++ = p->name;

	    /* End the list with a null-pointer */
	    *q = (char *) NULL;
	}


	/* Return a pointer to the list we've built */
	return(list);
}

/*
 *  char **makealiaslist(devices)
 *	char  **devices		List of aliases
 *
 *	Builds a list of aliases of the devices in the "devices"
 *	list.  This list will be terminated by (char *) NULL and
 *	will have the same number of elements as "devices".  If
 *	a device couldn't be found, that alias will be "".  There
 *	will be a one-to-one correspondence of devices to aliases
 *	in the device list "devices" and the generated list.
 *
 *  Arguments:
 *	devices		The list of devices to derive aliases from
 *
 *  Returns:  char **
 *	The address of the list of addresses of aliases.  The list
 *	and aliases will be allocated using the malloc() function.
 */

static	char **
makealiaslist(devices)
	char  **devices;
{
	/*  Automatic data  */
	char	      **pp;		/* Running ptr to (char *) */
	char	      **qq;		/* Running ptr to (char *) */
	char          **aliases;	/* List being returned */
	char	       *alias;		/* Alias of current device */
	int		olderrno;	/* Value of errno on entry */
	int		noerror;	/* Flag, TRUE if all's well */
	int		n;		/* Count of entries in "devices" */


	noerror = TRUE;
	olderrno = errno;
	if (devices) {

	    /* Get the number of entries in the constaint list */
	    for (n = 1 , pp = devices ; *pp ; pp++) n++;

	    /* Get space for the alias list */
	    if (aliases = (char **) malloc(n*sizeof(char *))) {

		/* Build the alias list */
		qq = aliases;
		for (pp = devices ; noerror && *pp ; pp++) {

		    /* Get the device's alias and put it in the list */
		    if (alias = devattr(*pp, DTAB_ALIAS)) *qq++ = alias;
		    else {
			errno = olderrno;
			if (alias = (char *) malloc(strlen("")+1))
			    *qq++ = strcpy(alias, "");
			else {
			    /* No space for a null string?  Yeech... */
			    for (qq = aliases ; *qq ; qq++) free(*qq);
			    free((char *) aliases);
			    aliases = (char **) NULL;
			    noerror = FALSE;
			}
		    }
		}
		if (noerror) *qq = (char *) NULL;

	    } else noerror = FALSE;  /* malloc() failed */

	} else aliases = (char **) NULL;  /* No constraint list */

	/* Return ptr to generated list or NULL if none or error */
	return(aliases);
}

/*
 *  void freealiaslist(aliaslist)
 *	char  **aliaslist;
 *
 *	Free the space allocated to the aliaslist.  It frees the space
 *	allocated to the character-strings referenced by the list then
 *	it frees the list.
 *
 *  Arguments:
 *	aliaslist	The address of the first item in the list of
 *			aliases that is to be freed
 *
 *  Returns:  void
 */

static	void
freealiaslist(aliaslist)
	char  **aliaslist;		/* Ptr to new device list */
{
	/* Automatic Data */
	char   **pp;			/* Running pointer */

	/* If there's a list ... */
	if (aliaslist) {

	    /* For each entry in the old list, free the entry */
	    for (pp = aliaslist ; *pp ; pp++) free(*pp);

	    /* Free the list */
	    free(aliaslist);
	}
}

/*
 *  char *getnextmatch(criteria, options)
 *	struct srch	       *criteria
 *	int			options
 *
 *  	Gets the next device in the device table that matches the criteria.
 *	Returns the alias of that device.
 *
 *  Arguments:
 *	criteria	The linked list of criteria to use to match a device
 *	options		Options modifying the criteria (only one that's really
 *			important is the DTAB_ANDCRITERIA flag)
 *
 *  Returns:  char *
 *	A pointer to a malloc()ed string containing the alias of the next
 *	device that matches the criteria, or (char *) NULL if none.
 */

static	char   *
getnextmatch(criteria, options)
	struct srch	       *criteria;
	int			options;
{
	/* Automatic data */
	struct devtabent       *devtabent;	/* Ptr to current record */
	char		       *alias;		/* Alias of device found */
	int			notdone;	/* Flag, done yet? */
	int			noerror;	/* Flag, had an error yet? */


	/*
	 *  Initializations:
	 *    -	No alias yet
	 *    - Not finished yet
	 *    -	Make sure there are criteria we're to use
	 */

	alias = (char *) NULL;
	notdone = TRUE;
	noerror = TRUE;

	/*  If we're to "and" the criteria...  */
	if (options & DTAB_ANDCRITERIA) {

	    /*
	     *  Search the device table until we've got a record that matches
	     *  all of the criteria or we run out of records
	     */

	    while (notdone && (devtabent = _getdevtabent())) {
		if (!devtabent->comment) {
		    if (!criteria || matchallcriteria(devtabent, criteria)) {
			if (alias = (char *) malloc((unsigned) strlen(devtabent->alias)+1))
			    (void) strcpy(alias, devtabent->alias);
			else noerror = FALSE;
			notdone = FALSE;
		    }
		}
		_freedevtabent(devtabent);
	    }
	}
	else {

	    /*
	     *  Search the device table until we've got a record that matches
	     *  any of the criteria or we run out of records
	     */

	    while (notdone && (devtabent = _getdevtabent())) {
		if (!devtabent->comment) {
		    if (!criteria || matchanycriteria(devtabent, criteria)) {
			if (alias = (char *) malloc((unsigned) strlen(devtabent->alias)+1))
			    (void) strcpy(alias, devtabent->alias);
			else noerror = FALSE;
			notdone = FALSE;
		    }
		}
		_freedevtabent(devtabent);
	    }
	}


	/* Return pointer to extracted alias (or NULL if none) */
	if ((alias == (char *) NULL) && noerror) errno = ENOENT;
	return(alias);
}

/*
 * int matchallcriteria(devtabent, criteria)
 *
 *	This function examines the record contained in "devtabent" and
 *	determines if that record meets all of the criteria specified by
 *	"criteria".
 *
 * Arguments:
 *	struct devtabent *devtabent	The device table entry to examine.
 *	struct srch    *criteria	The criteria to match.
 *
 * Returns:	int
 *	Returns TRUE if the record matches criteria, FALSE otherwise.
 */

static	int
matchallcriteria(ent, criteria)
	struct devtabent       *ent;		/* Entry to check */
	struct srch	       *criteria;	/* Criteria governing match */
{
	/* Automatic data */
	struct srch    *p;		/* Pointer to current criteria */
	struct attrval *q;		/* Pointer to current attr/val pair */
	int		notfound;	/* TRUE if attr found in list */
	int		failed;		/* TRUE if record failed to match */


	/* Test only if there's criteria to test against */
	if (criteria && (criteria->fcn != ENDLIST)) {

	    failed = FALSE;
	    for (p = criteria ; !failed && (p->fcn != ENDLIST) ; p++) {

		/* Don't compare against this criteria if it's function is
		 * "IGNORE" */
		if (p->fcn != IGNORE) {
		    if (p->fcn != NOEXISTS) {

			/*  Alias?  */
			if (strcmp(p->name, DTAB_ALIAS) == 0)
			    failed = !matches(ent->alias, p->cmp, p->fcn);

			/*  Char special device?  */
			else if (strcmp(p->name, DTAB_CDEVICE) == 0)
			    failed = !matches(ent->cdevice, p->cmp, p->fcn);

			/*  Block special device?  */
			else if (strcmp(p->name, DTAB_BDEVICE) == 0)
			    failed = !matches(ent->bdevice, p->cmp, p->fcn);

			/*  Pathname?  */
			else if (strcmp(p->name, DTAB_PATHNAME) == 0)
			    failed = !matches(ent->pathname, p->cmp, p->fcn);

			/*  Check other attributes...  */
			else {
			    notfound = TRUE;
			    q = ent->attrlist;
			    while (notfound && q) {
				if (strcmp(p->name, q->attr) == 0) {
				    notfound = FALSE;
				    if (!matches(q->val, p->cmp, p->fcn))
					failed = TRUE;
				}
				else q = q->next;
			    }
			    if (notfound) failed = TRUE;
			}
		    } 
		    else {
			if (strcmp(p->name, DTAB_ALIAS) == 0) failed = TRUE;
			else if (strcmp(p->name, DTAB_CDEVICE) == 0) failed = FALSE;
			else if (strcmp(p->name, DTAB_BDEVICE) == 0) failed = FALSE;
			else if (strcmp(p->name, DTAB_PATHNAME) == 0) failed = FALSE;
			else {
			    q = ent->attrlist;
			    while (!failed && q) {
				if (strcmp(p->name, q->attr) == 0) failed = TRUE;
				else q = q->next;
			    }
			}
		    }

		}  /* Search function is not "IGNORE" */

	    }  /* for loop, checking each criteria */

	}  /* if (criteria) */

	else failed = FALSE;  /* No criteria specified, it's a match */


	/* Return a value indicating if the record matches all criteria */
	return(!failed);
}

/*
 * int matchanycriteria(devtabent, criteria)
 *
 *	This function examines the record contained in "devtabent" and
 *	determines if that record meets any of the criteria specified by
 *	"criteria".
 *
 * Arguments:
 *	struct devtabent *devtabent	The device table entry to examine.
 *	struct srch      *criteria	The criteria to match.
 *
 * Returns:	int
 *	Returns TRUE if the record matches criteria, FALSE otherwise.
 */

static	int
matchanycriteria(ent, criteria)
	struct devtabent       *ent;		/* Entry to check */
	struct srch	       *criteria;	/* Criteria governing match */
{
	/* Automatic data */
	struct srch    *p;		/* Pointer to current criteria */
	struct attrval *q;		/* Pointer to current attr/val pair */
	int		matched;	/* FLAG: TRUE if record matched */
	int		found;		/* FLAG: TRUE if attribute found */


	/* Test only if there's criteria to test against */
	if (criteria && (criteria->fcn != ENDLIST)) {

	    matched = FALSE;
	    for (p = criteria ; !matched && (p->fcn != ENDLIST) ; p++) {

		/* Don't compare against this criteria if it's function is
		 * "IGNORE" */
		if (p->fcn != IGNORE) {
		    if (p->fcn != NOEXISTS) {

			/*  Alias?  */
			if (strcmp(p->name, DTAB_ALIAS) == 0)
			    matched = matches(ent->alias, p->cmp, p->fcn);

			/*  Char special device?  */
			else if (strcmp(p->name, DTAB_CDEVICE) == 0)
			    matched = matches(ent->cdevice, p->cmp, p->fcn);

			/*  Block special device?  */
			else if (strcmp(p->name, DTAB_BDEVICE) == 0)
			    matched = matches(ent->bdevice, p->cmp, p->fcn);

			/*  Pathname?  */
			else if (strcmp(p->name, DTAB_PATHNAME) == 0)
			    matched = matches(ent->pathname, p->cmp, p->fcn);

			/*  Check other attributes...  */
			else {
			    q = ent->attrlist;
			    found = FALSE;
			    while (!found && q)
				if (strcmp(p->name, q->attr) == 0) {
				    matched = matches(q->val, p->cmp, p->fcn);
				    found = TRUE;
				}
				else q = q->next;
			}
		    } 
		    else {
			if (strcmp(p->name, DTAB_ALIAS) == 0) matched = FALSE;
			else if (strcmp(p->name, DTAB_CDEVICE) == 0) matched = FALSE;
			else if (strcmp(p->name, DTAB_BDEVICE) == 0) matched = FALSE;
			else if (strcmp(p->name, DTAB_PATHNAME) == 0) matched = FALSE;
			else {
			    q = ent->attrlist;
			    matched = TRUE;
			    while (matched && q) {
				if (strcmp(p->name, q->attr) == 0) matched = FALSE;
				else q = q->next;
			    }
			}
		    }
		}  /* Search function is not "IGNORE" */

	    }  /* for loop, checking each criteria */

	}  /* if (criteria) */

	else matched = TRUE;  /* No criteria specified, it's a match */


	/* Return a value indicating if the record matches all criteria */
	return(matched);
}

/*
 *  int matches(value, compare, function)
 *	char   *value
 *	char   *compare
 *	int	function
 *
 *	This function sees if the operation <function> is satisfied by
 *	comparing the value <value> with <compare>.  It returns TRUE
 *	if so, FALSE otherwise.
 *
 *  Arguments:
 *	value		Value to compare
 *	compare		Value to compare against
 *	function	Function to be satisfied
 *
 *  Returns:  int
 *	TRUE if the function is satisfied, FALSE otherwise
 */

static	int
matches(value, compare, function)
	char   *value;
	char   *compare;
	int	function;
{
	/*  Automatic data  */
	int	rtn;		/* Value to return */


	if (value == NULL)
		value = "";

	/* Do case depending on the function */
	switch(function) {

	/* attr=val */
	case EQUAL:
	    rtn = (strcmp(value, compare) == 0);
	    break;

	/* attr!=val */
	case NOTEQUAL:
	    rtn = (strcmp(value, compare) != 0);
	    break;

	/* attr:* */
	case EXISTS:
	    rtn = TRUE;
	    break;

	/* attr!:* */
	case NOEXISTS:
	    rtn = FALSE;
	    break;

	/* Shouldn't get here... */
	default:
	    rtn = FALSE;
	    break;
	}

	/* Return a value indicating if the match was made */
	return(rtn);
}
