root/usr/src/lib/libadm/common/getdev.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */


/*
 * Copyright (c) 1997, by Sun Microsystems, Inc.
 * All rights reserved.
 */

/*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"
#include        <stdlib.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) */
};


/*
 * 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(char   *, char  **, char  **, int);
static  void            initdevicelist(void);
static  void            freedevicelist(void);
static  int             addtodevicelist(char *);
static  struct srch     *buildsearchlist(char **);
static  void            freesearchlist(struct srch *);
static  char            **buildreturnlist(void);
static  char            **makealiaslist(char **);
static  void            freealiaslist(char **);
static  char            *getnextmatch(struct srch *, int);
static  int             matchallcriteria(struct devtabent *, struct srch *);
static  int             matchanycriteria(struct devtabent *, struct srch *);
static  int             matches(char *, char *, int);


/*
 * 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(
        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 (NULL);

        /*  Build the search list  */
        if (criteria) {
            if (!(searchlist = buildsearchlist(criteria)))
                return (NULL);
        } else searchlist = 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(
        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) ?
                    NULL : *devices;
            else
                rtnval = (options & DTAB_EXCLUDEFLAG) ?
                    devtabentry : 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(void)
{
        /* Make the list a null list */
        (devicelist.head)->next = 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(void)
{
        /* 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(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) {
            q = p->next;
            if (!q) 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 = malloc(sizeof (struct deviceent))) {

                /* Alloc space for the device character string */
                if (str = malloc(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 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(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 = 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 = 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 = malloc(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;
            }
        }

        /* 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(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(void)
{
        /* Automatic data */
        char                    **list;
        char                    **q;
        struct deviceent        *p;


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

        if (list = 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 = 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(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 = 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 = malloc(strlen("")+1))
                            *qq++ = strcpy(alias, "");
                        else {
                            /* No space for a null string?  Yeech... */
                            for (qq = aliases; *qq; qq++) free(*qq);
                            free(aliases);
                            aliases = NULL;
                            noerror = FALSE;
                        }
                    }
                }
                if (noerror)
                        *qq = NULL;

            }

        } else
                aliases = 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(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(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 = 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 = malloc(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 = malloc(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 == 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(
        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(
        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(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);
}