/*	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:getdgrp.c	1.1.3.1"

/*LINTLIBRARY*/

/*
 *  getdgrp.c
 *
 * Contains the following global functions:
 *	getdgrp()	Get the device groups that meet certain criteria.
 */

/*
 *  Header Files Referenced
 *	<sys/types.h>		Data Types
 *	<stdio.h>		Standard I/O definitions
 *	<string.h>		Character-string definitions
 *	<devmgmt.h>		Definitions for accessing device table files
 *	"devtab.h"		Local definitions for device tables
 */

#include	<sys/types.h>
#include	<stdio.h>
#include	<string.h>
#include	<devmgmt.h>
#include	"devtab.h"


/*
 *  Externals Referenced (and not defined by header files):
 *	malloc()		Allocate a block of main memory
 *	free()			Free a malloc()ed block of main memory
 *	getdev()		Get the list of devices that meet a list of 
 *				criteria
 *	devattr()		Get a device's attribute
 *	oam_dgroup		Open file descriptor for the device-group table
 */

extern	void   *malloc();
extern	void	free();	
extern	char  **getdev();
extern	char   *devattr();
extern	FILE   *oam_dgroup;

/*
 *  Local definitions
 *	struct dgrplist		Structure that makes up the internal device 
 *				group list
 *				Members:
 *				    name	Name of the device group
 *				    next	Pointer to the next in the list
 */

struct dgrplist {
	char		       *name;	
	struct dgrplist	       *next;
};


/*
 *  Local functions
 *	initdgrplist		Initialize the internal device group list
 *	addtodgrplist		Add a device group to the device group list
 *	isindevlist		Does the device group contain a device?
 *	isincallerslist		Is a device group in the caller's list?
 *	buildreturnlist		Build list of device groups to return
 *	freedgrplist		Free the internal device group list
 */

static	void	initdgrplist();
static	int	addtodgrplist();
static	int	isindevlist();
static	int	isincallerslist();	
static	char  **buildreturnlist();
static	void	freedgrplist();	


/*
 *  Local data
 *	dgrplistfirst	First (dummy) node in the device group list
 *	dgrplistcount	Number of items in the device group list
 */

static	struct dgrplist	dgrplistfirst;
static	int		dgrplistcount;

/*
 * char **getdgrp(dgroups, criteria, options)
 *	char  **dgroups
 *	char  **criteria
 *	int	options
 *
 *	This function compiles a list of device groups containing devices
 *	that meet certain criteria and returns a pointer to the first
 *	item in that list.
 *
 *  Arguments:
 *	dgroups		The list of device groups to choose from or the list
 *			of device groups to exclude from the list (depends on
 *			"options"
 *	criteria	The criteria that a device must meet
 *	options		Indicates 1) whether to "and" the criteria or to "or"
 *			the criteria, 2) indicates whether to limit the
 *			generated list to "dgroups" or to exclude those
 *			device-groups from the list, 3) to list all device groups
 *			even if they don't contain valid devices.
 *
 *  Returns:  char **
 *	A pointer to the first address in the list of addresses of generated
 *	device groups
 */

char **
getdgrp(dgroups, criteria, options)
	char  **dgroups;	/* List of device groups */
	char  **criteria;	/* List of criteria to meet */
	int	options;	/* Options governing the search */
{
	/*  Automatic data  */
	char		      **devlist;	/* Devices that meet criteria */
	char		      **plist;		/* Device groups to return */
	struct dgrptabent      *dgrp;		/* Dgrp information struct */
	int			errorflag;	/* TRUE if error occurred */
	int			listallflag;	/* TRUE if DTAB_LISTALL && (!criteria || !*criteria) */


	/*  
	 *  Open the device-group table if needed  
	 */

	if (!oam_dgroup && !_opendgrptab("r")) return(char **) NULL;


	/*  
	 *  Get the list of devices that meet the criteria specified
	 *  This step can be skipped if DTAB_LISTALL is requested and
	 *  there is no criteria list.
	 */

	if (((options & DTAB_LISTALL) == 0) || (criteria && *criteria)) {
	    devlist = getdev((char **) NULL, criteria, (options&DTAB_ANDCRITERIA));
	    listallflag = FALSE;
	} else {
	    devlist = (char **) NULL;
	    listallflag = TRUE;
	}


	/*  
	 *  Initialize the device group list (contains the device groups 
	 *  we're accumulating)  
	 */

	errorflag = FALSE;
	initdgrplist();


	/*  
	 *  If no device groups were specified by the caller, accumulate all 
	 *  device groups 
	 */

	_setdgrptab();
	if (!dgroups || !(*dgroups)) {
	    while (!errorflag && (dgrp = _getdgrptabent())) {
		if (!dgrp->comment && (listallflag || isindevlist(dgrp, devlist)))
		    errorflag = !addtodgrplist(dgrp);
		_freedgrptabent(dgrp);
	    }
	}
	
	else {
	
	    /*  
	     *  If the exclusion flag is not set, build a list of device 
	     *  groups that is a subset of those specified by the caller 
	     */

	    if ((options & DTAB_EXCLUDEFLAG) == 0) {
		while (!errorflag && (dgrp = _getdgrptabent())) {
		    if (!dgrp->comment && isincallerslist(dgrp, dgroups) && 
			(listallflag || isindevlist(dgrp, devlist))) {
			errorflag = !addtodgrplist(dgrp);
		    }
		    _freedgrptabent(dgrp);
		}
	    }

	    /*
	     *  If the exclusion flag is set, build a list of device groups 
	     *  that meet the criteria and are not in the list of device 
	     *  groups specified by the caller.
	     */
	    else {
		while (!errorflag && (dgrp = _getdgrptabent())) {
		    if (!dgrp->comment && !isincallerslist(dgrp, dgroups) &&
			(listallflag || isindevlist(dgrp, devlist))) {
		    	errorflag = !addtodgrplist(dgrp);
		    }
		    _freedgrptabent(dgrp);
		}
	    }
	}
	plist = buildreturnlist();
	freedgrplist();
	_enddgrptab();
	return(plist);
}

/*
 *  int initdgrplist()
 *
 *	Initializes the internal device group linked list
 *
 *  Arguments:  None
 *
 *  Returns:  void
 */

static void	
initdgrplist()
{
	/*  Automatic data  */
	
	/*  Initialize the structure.  Dummy node points to nothing, count to zero.  */
	dgrplistcount = 0;
	dgrplistfirst.name = "";
	dgrplistfirst.next = (struct dgrplist *) NULL;
}

/*
 *  int addtodgrplist(dgrp)
 *	struct dgrptabent *dgrp
 *
 *	Adds the device group described by the "dgrp" structure to the
 *	internal list of device-groups we're accumulating.
 *
 *  Arguments:
 *	dgrp	Describes the device-group we're adding
 *
 *  Returns: int
 *	TRUE if successful, FALSE otherwise
 */

static int	
addtodgrplist(dgrp)
	struct dgrptabent *dgrp;
{
	/*  Automatic data  */
	struct dgrplist	       *newnode;	/* Allocated node */
	struct dgrplist	       *p;		/* Running dgrp list ptr */
	struct dgrplist	       *q;		/* Another Running dgrp list ptr */
	char		       *newstr;		/* Space for the dgroup name */
	int			errorflag;	/* TRUE if error */
	int			cmpval;		/* Value from strcmp() */

	/*  No errors seen yet  */
	errorflag = FALSE;

	/*  Find where we're supposed to insert this item in the list  */
	q = &dgrplistfirst;
	p = q->next;
	while (p && ((cmpval = strcmp(p->name, dgrp->name)) < 0)) {
	    q = p;
	    p = p->next;
	}

	/*  If the item isn't already in the list, insert it  */
	if ((p == (struct dgrplist *) NULL) || (cmpval != 0)) {

	    /* Allocate space for the structure */
	    newnode = (struct dgrplist *) malloc(sizeof(struct dgrplist));
	    if (newnode) {

		/* Allocate space for the device group name */
		if (newstr = (char *) malloc((unsigned) strlen(dgrp->name)+1)) {

		    /* Link the new structure into the list */
		    newnode->name = strcpy(newstr, dgrp->name); 
		    newnode->next = p;
		    q->next = newnode;
		    dgrplistcount++;
		} 
		else {
		    /* No space for the string.  Clean up */
		    errorflag = TRUE;
		    free(newnode);
		}
	    } else errorflag = TRUE;
	}

	/* Return a value that indicates whether we've had an error */
	return(!errorflag);
}

/*
 *  int isindevlist(dgrp, devlist)
 *	struct dgrptabent *dgrp
 *	char		 **devlist
 *
 *	This function searches the device membership list of the device
 *	group <dgrp> for any of the devices listed in the list of devices
 *	<devlist>.  It returns TRUE if at least one device in <devlist> is
 *	found in <dgrp>, otherwise it returns false.
 *
 *  Arguments:
 *	dgrp		The device group to examine
 *	devlist		The list of devices to search for
 *
 *  Returns:  int
 *	TRUE if one of the devices in <devlist> is a member of the device
 *	group <dgrp>, FALSE otherwise
 */

static int	
isindevlist(dgrp, devlist)
	struct dgrptabent *dgrp;	/* Dgrp to search for */
	char	         **devlist;	/* List of devices to search against */
{
	/*  Automatic data  */
	struct member  *pmbr;		/* Next member of the dgrp list */
	char	      **pdev;		/* Next device in the dev list */
	char	       *mbralias;	/* The alias of a group member */
	int		cmpval;		/* strcmp() result */
	int		notfound;	/* TRUE if no mbr of dgrp is in dev list */
	int		allocflag;	/* TRUE if the mbralias string is malloc()ed */


	/*
	 *  For each device in the device group, search the alphabetically 
	 *  sorted list of devices for that device.
	 */

	notfound = TRUE;
	for (pmbr = dgrp->membership ; notfound && pmbr ; pmbr = pmbr->next) {

	    /* Get the member's alias (we've got it if the member is not a pathname) */
	    if (allocflag = (*pmbr->name == '/')) mbralias = devattr(pmbr->name, DTAB_ALIAS);
	    else mbralias = pmbr->name;

	    /* If we've got a member alias, search the device list for it */
	    if (mbralias) for (pdev = devlist ; notfound && *pdev ; pdev++) 
		if ((cmpval = strcmp(mbralias, *pdev)) == 0) notfound = FALSE;
		else if (cmpval < 0) break;	/* Optimization:  alpha sorted list */

	    /* Free the space allocated to the member alias 
	     * (if it was allocated above by devattr()) */
	    if (allocflag) free(mbralias);
	    
	}


	/*  
	 *  Return a value indicating that we the device group contains 
	 *  a member that is in the list of devices 
	 */

	return(!notfound);
}

/*
 * int isincallerslist(dgrp, dgroups)
 *	struct dgrptabent *dgrp
 *	char		 **dgroups
 *
 *	This function looks through the "dgroups" list for the device
 *	group described by "dgrp"
 *
 *  Arguments:
 *	dgrp		Device group to search for
 *	dgroups		The address of the first item in the list of device 
 *			groups to search
 *
 *  Returns:  int
 *	TRUE if found, FALSE otherwise
 */

static int	
isincallerslist(dgrp, dgroups)
	struct dgrptabent *dgrp;	/* Dgrp to search for */
	char	         **dgroups;	/* Caller's list of dgroups */
{
	/*  Automatic data  */
	char          **pdgrp;
	int		notfound;

	/* 
	 *  Search the list of device groups for the name of the device group 
	 *  in the structure described by <dgrp>.
	 */

	/*  Initializations  */
	notfound = TRUE;

	/*  Search the device group list for name of this device group  */
	for (pdgrp = dgroups ; notfound && *pdgrp ; pdgrp++) {
	    if (strcmp(dgrp->name, *pdgrp) == 0) notfound = FALSE;
	}

	/*  Return TRUE if the device group is in the list, FALSE otherwise  */
	return(!notfound);
}

/*
 *  char **buildreturnlist()
 *
 *	This function builds the list of pointers to device groups
 *	to return to the caller from the linked list of device-groups
 *	we've been accumulating.
 *
 *  Arguments:  none
 *
 *  Returns: char **
 *	A pointer to the first element in the malloc()ed list of pointers
 *	to malloc()ed character strings containing device groups which have
 *	member devices which match the criteria
 */

static char	      **
buildreturnlist()
{
	char	      	      **list;		/* List being built */
	char		      **pp;		/* Temp ptr within list */
	struct dgrplist	       *pdgrpent;	/* Ptr into list of dgrps to return */
				
	/*  Allocate space for the list of pointers to device groups */
	list = (char **) malloc((dgrplistcount+1)*sizeof(char *));

	/*  For each item in the device group list, put an entry in the 
	 *  list of names we're building  */
	if (pp = list) {
	    for (pdgrpent = dgrplistfirst.next ; pdgrpent ; pdgrpent = pdgrpent->next) {
		*pp++ = pdgrpent->name;
	    }
	    /*  The list ends with a null pointer  */
	    *pp = (char *) NULL;
	}

	/*  Return a pointer to the allocated list  */
	return(list);
}

/*
 *  void freedgrplist()
 *
 *	This function frees the resources allocated to the internal
 *	linked list of device groups
 *
 *  Arguments:  none
 *
 *  Returns:  void
 */

static void		
freedgrplist()
{
	struct dgrplist	       *pdgrpent;	/* Dgrp to free */
	struct dgrplist	       *nextnode;	/* Next one to free */

	for (pdgrpent = dgrplistfirst.next ; pdgrpent ; pdgrpent = nextnode) {
	    nextnode = pdgrpent->next;
	    free(pdgrpent);
	}
}
