xref: /illumos-gate/usr/src/lib/libadm/common/getdev.c (revision 0fb96ba1f1ce26ff8b286f8f928769a6afcb00a6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright (c) 1997, by Sun Microsystems, Inc.
28  * All rights reserved.
29  */
30 
31 /*LINTLIBRARY*/
32 
33 /*
34  *  getdev.c
35  *
36  *  Contents:
37  *	getdev()	List devices that match certain criteria.
38  */
39 
40 /*
41  * Header files referenced:
42  *	<sys/types.h>	System Data Types
43  *	<errno.h>	Error handling
44  *	<fcntl.h>	File controlling
45  *	<ctype.h>	Character types
46  *	<string.h>	String handling
47  *	<devmgmt.h>	Global device-management def'ns
48  *	"devtab.h"	Local device-management dev'ns
49  */
50 
51 #include	<sys/types.h>
52 #include	<errno.h>
53 #include	<fcntl.h>
54 #include	<ctype.h>
55 #include	<string.h>
56 #include	<devmgmt.h>
57 #include	"devtab.h"
58 #include	<stdlib.h>
59 
60 /*
61  * Local definitions
62  *	NULL		Nil address
63  *	TRUE		Boolean TRUE
64  *	FALSE		Boolean FALSE
65  */
66 
67 #ifndef	NULL
68 #define	NULL			0
69 #endif
70 
71 #ifndef	TRUE
72 #define	TRUE			('t')
73 #endif
74 
75 #ifndef	FALSE
76 #define	FALSE			0
77 #endif
78 
79 
80 /*
81  *  Comparison values.  These values are placed in the struct srch
82  *  structure by buildsearchlist() and are used to compare values
83  *  in matches().
84  *	EQUAL		Attribute must equal this value
85  *	NOTEQUAL	Attribute must not equal this value
86  *	EXISTS		Attribute must exist
87  *	NOEXISTS	Attribute must not exist
88  *	IGNORE		Ignore this entry
89  *	ENDLIST		This entry ends the list
90  */
91 
92 #define	EQUAL			1
93 #define	NOTEQUAL		2
94 #define	EXISTS			3
95 #define	NOEXISTS		4
96 #define	IGNORE			5
97 #define	ENDLIST			0
98 
99 
100 /*
101  *  Structure definitions:
102  * 	deviceent	Defines a device that matches criteria
103  *	srch		Describes a criteria
104  */
105 
106 struct deviceent {
107 	struct deviceent	*next;	/* Pointer to next item in the list */
108 	char			*name;	/* Presentation name of the device */
109 };
110 
111 struct srch {
112 	char   *name;			/* Name of field to compare */
113 	char   *cmp;			/* Value to compare against */
114 	int	fcn;			/* Type of comparison (see above) */
115 };
116 
117 
118 /*
119  * Local functions referenced
120  *	oktoaddtolist()		Determines if device can be added to the
121  *				list by examining the devices list and
122  *				the options governing the search
123  *	initdevicelist()	Initializes the linked list of devices
124  *				to be included in the list-to-return
125  *	freedevicelist()	Frees the resources allocated to the linked
126  *				list of devices
127  *	addtodevicelist()	Adds an entry to the linked list of devices
128  *	buildsearchlist()	Builds a list of struct srch structures from
129  *				the criteria strings
130  *	freesearchlist()	Frees the resources allocated to the list of
131  *				struct srch structures
132  *	buildreturnlist()	Builds the list of devices to return from the
133  *				linked list of devices we've accumulated
134  *	makealiaslist()		Builds a list of aliases from the list of
135  *				devices presented by the caller
136  *	freealiaslist()		Frees the resources allocated to the list of
137  *				devices aliases
138  *	getnextmatch()		Get the next device that matches the search
139  *				criteria
140  *	matchallcriteria()	See if the device attributes match all of the
141  *				search criteria
142  *	matchanycriteria()	See if the device attributes match any of the
143  *				search criteria
144  *	matches()		See if the criteria and attribute match
145  */
146 
147 static	char		*oktoaddtolist(char   *, char  **, char  **, int);
148 static	void		initdevicelist(void);
149 static	void		freedevicelist(void);
150 static	int		addtodevicelist(char *);
151 static	struct srch	*buildsearchlist(char **);
152 static	void 		freesearchlist(struct srch *);
153 static	char		**buildreturnlist(void);
154 static	char		**makealiaslist(char **);
155 static	void		freealiaslist(char **);
156 static	char		*getnextmatch(struct srch *, int);
157 static	int		matchallcriteria(struct devtabent *, struct srch *);
158 static	int		matchanycriteria(struct devtabent *, struct srch *);
159 static	int		matches(char *, char *, int);
160 
161 
162 /*
163  * Global Data
164  */
165 
166 /*
167  * Static Data
168  *	devicelisthead	The first item (dummy) in the linked list of devices
169  *			we're building
170  *	devicelist	Structure describing the linked list of devices
171  */
172 
173 static	struct deviceent	devicelisthead;
174 static	struct {
175 	struct deviceent	*head;
176 	int			count;
177 } devicelist = {&devicelisthead, 0};
178 
179 /*
180  *  char **getdev(devices, criteria, options)
181  *	char  **devices
182  *	char  **criteria
183  *	int	options
184  *
185  *	This function builds a list of devices that match criteria,
186  *	governed by the device list.
187  *
188  *  Arguments:
189  *	devices		The list of devices to select from or the list of
190  *			devices to exclude, depending on the value of
191  *			"options"
192  *	criteria	The list of criteria governing the device selection
193  *			Of the form <attr><op><val>
194  *	options		Options controlling the device selection.  May require
195  *			that a device meet all of the criteria (default is
196  *			any one of the criteria), or may require that the
197  *			devices in the list of devices be excluded from the
198  *			generated list (default is to select only those
199  * 			devices in the list)
200  *
201  *  Returns:  char **
202  *	The address of the first item in the list of devices that meet
203  *	the selection criteria
204  */
205 
206 char  **
207 getdev(
208 	char  **devices,		/* List of devices to constrain */
209 	char  **criteria,		/* List of selection criteria */
210 	int	options)		/* Options governing the search */
211 {
212 	/* Automatic data */
213 	char		**aliases;	/* List of constraining devices */
214 	char		**returnlist;	/* List of ptrs to aliases to return */
215 	struct srch	*searchlist;	/* Pointer to searching criteria */
216 	char		*entry;		/* Pointer to alias in record */
217 	int		errflag;	/* FLAG:  TRUE if error */
218 
219 
220 	/*
221 	 *  Initializations
222 	 */
223 
224 	/*  Make sure the exclude/include list is all aliases */
225 	aliases = makealiaslist(devices);
226 	if (devices && !aliases)
227 		return (NULL);
228 
229 	/*  Build the search list  */
230 	if (criteria) {
231 	    if (!(searchlist = buildsearchlist(criteria)))
232 		return (NULL);
233 	} else searchlist = NULL;
234 
235 	/*  Initialize searching  */
236 	initdevicelist();
237 	_setdevtab();
238 
239 
240 	/*
241 	 *  Keep on going until we get no more matches
242 	 */
243 
244 	errflag = FALSE;
245 	while (!errflag && (entry = getnextmatch(searchlist, options))) {
246 	    if (entry = oktoaddtolist(entry, devices, aliases, options)) {
247 		errflag = addtodevicelist(entry);
248 	    }
249 	}
250 
251 
252 	/*
253 	 *  Clean up:
254 	 *    -	Free the entry space we've allocated.
255 	 *    -	Close the device table.
256 	 *    - Build the list to return to the caller.
257 	 *    - Free the accumulate device space (but not the strings!)
258 	 *    - Free the alias list
259 	 *    - Return the built list to the caller.
260 	 */
261 
262 	returnlist = buildreturnlist();
263 	freedevicelist();
264 	freealiaslist(aliases);
265 	_enddevtab();
266 	return (returnlist);
267 }
268 
269 /*
270  *  char *oktoaddtolist(devtabentry, devices, aliases, options)
271  *	char   *devtabentry
272  *	char  **devices
273  *	char  **aliases
274  *	int	options
275  *
276  *	This function determines the device "devtabentry" can be
277  *	added to the list of devices we're accumulating.  If so,
278  *	it returns the device name (not the alias).
279  *
280  *  Arguments:
281  *	devtabentry	The device alias that may or may not belong in the
282  *			list we're building.
283  *	devices		The devices specified by the caller
284  *	aliases		The aliases of the devices specified by the caller
285  *			(1-1 correspondence with "devices")
286  *	options		Options controlling the search
287  */
288 
289 static	char *
290 oktoaddtolist(
291 	char   *devtabentry,	/* Alias to check against list */
292 	char  **devices,	/* List of devices to check against */
293 	char  **aliases,	/* List of alias of those devices */
294 	int	options)	/* Options governing search */
295 {
296 	/* Automatic data */
297 	char   *rtnval;		/* Value to return */
298 	int	found;		/* Flag:  TRUE if found */
299 
300 	/* If there's a constraint list, is this device in it? */
301 	if (devices && aliases) {
302 
303 	    /* Set "found" to TRUE if the device is in the list */
304 	    found = FALSE;
305 	    while (!found && *aliases) {
306 		if (strcmp(devtabentry, *aliases) == 0) found = TRUE;
307 		else {
308 		    devices++;
309 		    aliases++;
310 		}
311 	    }
312 
313 	    /* Set value to return */
314 	    if (found)
315 		rtnval = (options & DTAB_EXCLUDEFLAG) ?
316 		    NULL : *devices;
317 	    else
318 		rtnval = (options & DTAB_EXCLUDEFLAG) ?
319 		    devtabentry : NULL;
320 
321 	} else rtnval = devtabentry;  /* No constraint list */
322 
323 	return (rtnval);
324 }
325 
326 /*
327  *  void initdevicelist()
328  *
329  *	This function initializes the list of accumulated devices.
330  *
331  *  Arguments:  None
332  *
333  *  Returns:  Void.
334  *
335  *  Notes:
336  */
337 
338 static	void
339 initdevicelist(void)
340 {
341 	/* Make the list a null list */
342 	(devicelist.head)->next = NULL;
343 	devicelist.count = 0;
344 }
345 
346 /*
347  *  void freedevicelist()
348  *
349  *	This function frees the resources allocated to the linked list of
350  *	devices we've been accumulating.
351  *
352  *  Arguments:  none
353  *
354  *  Returns:  void
355  */
356 
357 static	void
358 freedevicelist(void)
359 {
360 	/* Automatic data */
361 	struct deviceent	*pdevice;	/* Pointer to current entry */
362 	char			*freeblk;	/* Pointer space to free */
363 
364 	/* List has a dummy head node */
365 	pdevice = (devicelist.head)->next;
366 	while (pdevice) {
367 	    freeblk = (char *) pdevice;
368 	    pdevice = pdevice->next;
369 	    free(freeblk);
370 	}
371 }
372 
373 /*
374  *  int addtodevicelist(deventry)
375  *	char   *deventry
376  *
377  * 	This function adds the device <deventry> to the list of devices already
378  *	accumulated.  It will not add the device if that device already exists
379  *	in the list.  The function returns 0 if successful, -1 if not with
380  *	"errno" set (by functions called) to indicate the error.
381  *
382  *  Arguments:
383  *	deventry		char *
384  *				The name of the device to add to the list of
385  *				accumulated devices
386  *
387  *  Returns:
388  *	0	If successful
389  *	-1	If failed.  "errno" will be set to a value that indicates the
390  *		error.
391  *
392  *  Notes:
393  *    -	The memory allocation scheme has the potential to fragment the memory
394  *	in the malloc heap.  We're allocating space for a local structure,
395  *	which will be freed by getdev(), then allocating space for the device
396  *	name, which will be freed (maybe) by the application using getdev().
397  *	Not worrying about this at the moment.
398  */
399 
400 static	int
401 addtodevicelist(char *deventry)
402 {
403 	/* Automatic data */
404 	struct deviceent	*p;	/* Pointer to current device */
405 	struct deviceent	*q;	/* Pointer to next device */
406 	struct deviceent	*new;	/* Pointer to the alloc'd new node */
407 	char			*str;	/* Pointer to alloc'd space for name */
408 	int			rtncd;	/* Value to return to the caller */
409 	int			cmpcd;	/* strcmp() value, comparing names */
410 	int			done;	/* Loop control, TRUE if done */
411 
412 
413 	/* Initializations */
414 	rtncd = FALSE;
415 
416 
417 	/*
418 	 * Find the place in the found device list devicelist where this
419 	 * device is to reside
420 	 */
421 
422 	p = devicelist.head;
423 	done = FALSE;
424 	while (!done) {
425 	    q = p->next;
426 	    if (!q) done = TRUE;
427 	    else if ((cmpcd = strcmp(deventry, q->name)) <= 0) done = TRUE;
428 	    else p = q;
429 	}
430 
431 	/*
432 	 *  If the device is not already in the list, insert it in the list
433 	 */
434 
435 	if (!q || (cmpcd != 0)) {
436 
437 	    /* Alloc space for the new node */
438 	    if (new = malloc(sizeof (struct deviceent))) {
439 
440 		/* Alloc space for the device character string */
441 		if (str = malloc(strlen(deventry)+1)) {
442 
443 		/*
444 		 * Insert an entry in the found device list containing
445 		 * this device name
446 		 */
447 		    new->next = q;
448 		    p->next = new;
449 		    new->name = strcpy(str, deventry);
450 		    devicelist.count++;
451 		}
452 
453 		/* Couldn't alloc space for the device name.  Error. */
454 		else rtncd = TRUE;
455 	    }
456 
457 	    /* Couldn't alloc space for new node in the found list.  Error. */
458 	    else rtncd = TRUE;
459 
460 	}
461 
462 	/* Return an value indicating success or failure */
463 	return (rtncd);
464 }
465 
466 /*
467  *  struct srch *buildsearchlist(criteria)
468  *	char  **criteria
469  *
470  *	This function builds a list of search criteria structures from the
471  *	criteria strings in the list of criteria whose first argument is
472  *	specified by "criteria".
473  *
474  *  Arguments:
475  *	criteria	The address of the first item in a list of
476  *			character-strings specifying search criteria
477  *
478  *  Returns: struct srch *
479  *	The address of the structure in the list of structures describing the
480  *	search criteria.
481  *
482  *  Notes:
483  *    -	The only "regular expression" currently supported by the
484  *	kywd:exp and kywd!:exp forms is exp=*.  This function assumes
485  *	that kywd:exp means "if kywd exist" and that kywd!:exp means
486  *	"if kywd doesn't exist".
487  */
488 
489 static 	struct srch *
490 buildsearchlist(char **criteria)	/* Criteria from caller */
491 {
492 	/*  Automatic data  */
493 	struct srch	*rtnbuf;	/* Value to return */
494 	struct srch	*psrch;		/* Running pointer */
495 	char		*str;		/* Ptr to malloc()ed string space */
496 	char		*p;		/* Temp pointer to char */
497 	int		noerror;	/* TRUE if all's well */
498 	int		n;		/* Temp counter */
499 	char		**pp;		/* Running ptr to (char *) */
500 
501 
502 	/*  Initializations  */
503 	rtnbuf = NULL;				/* Nothing to return yet */
504 	noerror = TRUE;				/* No errors (yet) */
505 
506 	/* If we were given any criteria ... */
507 	if (criteria) {
508 
509 	    /* Count the number of criteria in the list */
510 	    for (n = 1, pp = criteria; *pp++; n++)
511 		;
512 
513 	    /* Allocate space for structures describing the criteria */
514 	    if (rtnbuf = malloc(n*sizeof (struct srch))) {
515 
516 		/* Build structures describing the criteria */
517 		pp = criteria;
518 		psrch = rtnbuf;
519 		while (noerror && *pp) {
520 
521 		    /* Keep list sane for cleanup if necessary */
522 		    psrch->fcn = ENDLIST;
523 
524 		    /* Alloc space for strings referenced by the structure */
525 		    if (str = malloc(strlen(*pp)+1)) {
526 
527 			/* Extract field name, function, and compare string */
528 			(void) strcpy(str, *pp);
529 
530 			/* If criteria contains an equal sign ('=') ... */
531 			if (p = strchr(str+1, '=')) {
532 			    if (*(p-1) == '!') {
533 				*(p-1) = '\0';
534 				psrch->fcn = NOTEQUAL;
535 			    } else {
536 				*p = '\0';
537 				psrch->fcn = EQUAL;
538 			    }
539 			    psrch->cmp = p+1;
540 			    psrch->name = str;
541 			    psrch++;
542 			}
543 
544 			/* If criteria contains a colon (':') ... */
545 			else if (p = strchr(str+1, ':')) {
546 			    if (*(p-1) == '!') {
547 				*(p-1) = '\0';
548 				psrch->fcn = NOEXISTS;
549 			    } else {
550 				*p = '\0';
551 				psrch->fcn = EXISTS;
552 			    }
553 			    psrch->cmp = p+1;
554 			    psrch->name = str;
555 			    psrch++;
556 			}
557 		    } else {
558 			/* Unable to malloc() string space.  Clean up */
559 			freesearchlist(rtnbuf);
560 			noerror = FALSE;
561 		    }
562 		    /* Next criteria */
563 		    pp++;
564 		}
565 		/* Terminate list */
566 		if (noerror) psrch->fcn = ENDLIST;
567 	    }
568 	}
569 
570 	/* Return a pointer to allocated space (if any) */
571 	return (rtnbuf);
572 }
573 
574 /*
575  *  void freesearchlist(list)
576  *	struct srch  *list
577  *
578  *	This function frees the resources allocated to the searchlist <list>.
579  *
580  *  Arguments:
581  *	list		The list whose resources are to be released.
582  *
583  *  Returns:  void
584  */
585 
586 static	void
587 freesearchlist(struct srch *list)
588 {
589 	/* Automatic data */
590 	struct srch		*psrch;		/* Running ptr to structs */
591 
592 
593 	/* Free all of the string space allocated for the structure elememts */
594 	for (psrch = list; psrch->fcn != ENDLIST; psrch++) {
595 	    free(psrch->name);
596 	}
597 
598 	/* Free the list space */
599 	free(list);
600 }
601 
602 /*
603  *  char **buildreturnlist()
604  *
605  *	This function builds a list of addresses of character-strings
606  *	to be returned from the linked-list of devices we've been
607  *	building.  It returns a pointer to the first item in that list.
608  *
609  *  Arguments:  none
610  *
611  *  Returns:  char **
612  *	The address of the first item in the return list
613  */
614 
615 static	char **
616 buildreturnlist(void)
617 {
618 	/* Automatic data */
619 	char			**list;
620 	char			**q;
621 	struct deviceent	*p;
622 
623 
624 	/*
625 	 * Allocate space for the return list,
626 	 * with space for the terminating node
627 	 */
628 
629 	if (list = malloc((devicelist.count+1)*sizeof (char *))) {
630 
631 	/*
632 	 * Walk the list of accumulated devices, putting pointers to
633 	 * device names in the list to return
634 	 */
635 
636 	    q = list;
637 	    for (p = devicelist.head->next; p; p = p->next) *q++ = p->name;
638 
639 	    /* End the list with a null-pointer */
640 	    *q = NULL;
641 	}
642 
643 
644 	/* Return a pointer to the list we've built */
645 	return (list);
646 }
647 
648 /*
649  *  char **makealiaslist(devices)
650  *	char  **devices		List of aliases
651  *
652  *	Builds a list of aliases of the devices in the "devices"
653  *	list.  This list will be terminated by (char *) NULL and
654  *	will have the same number of elements as "devices".  If
655  *	a device couldn't be found, that alias will be "".  There
656  *	will be a one-to-one correspondence of devices to aliases
657  *	in the device list "devices" and the generated list.
658  *
659  *  Arguments:
660  *	devices		The list of devices to derive aliases from
661  *
662  *  Returns:  char **
663  *	The address of the list of addresses of aliases.  The list
664  *	and aliases will be allocated using the malloc() function.
665  */
666 
667 static	char **
668 makealiaslist(char **devices)
669 {
670 	/*  Automatic data  */
671 	char		**pp;		/* Running ptr to (char *) */
672 	char		**qq;		/* Running ptr to (char *) */
673 	char		**aliases;	/* List being returned */
674 	char		*alias;		/* Alias of current device */
675 	int		olderrno;	/* Value of errno on entry */
676 	int		noerror;	/* Flag, TRUE if all's well */
677 	int		n;		/* Count of entries in "devices" */
678 
679 
680 	noerror = TRUE;
681 	olderrno = errno;
682 	if (devices) {
683 
684 	    /* Get the number of entries in the constaint list */
685 	    for (n = 1, pp = devices; *pp; pp++) n++;
686 
687 	    /* Get space for the alias list */
688 	    if (aliases = malloc(n*sizeof (char *))) {
689 
690 		/* Build the alias list */
691 		qq = aliases;
692 		for (pp = devices; noerror && *pp; pp++) {
693 
694 		    /* Get the device's alias and put it in the list */
695 		    if (alias = devattr(*pp, DTAB_ALIAS)) *qq++ = alias;
696 		    else {
697 			errno = olderrno;
698 			if (alias = malloc(strlen("")+1))
699 			    *qq++ = strcpy(alias, "");
700 			else {
701 			    /* No space for a null string?  Yeech... */
702 			    for (qq = aliases; *qq; qq++) free(*qq);
703 			    free(aliases);
704 			    aliases = NULL;
705 			    noerror = FALSE;
706 			}
707 		    }
708 		}
709 		if (noerror)
710 			*qq = NULL;
711 
712 	    }
713 
714 	} else
715 		aliases = NULL;  /* No constraint list */
716 
717 	/* Return ptr to generated list or NULL if none or error */
718 	return (aliases);
719 }
720 
721 /*
722  *  void freealiaslist(aliaslist)
723  *	char  **aliaslist;
724  *
725  *	Free the space allocated to the aliaslist.  It frees the space
726  *	allocated to the character-strings referenced by the list then
727  *	it frees the list.
728  *
729  *  Arguments:
730  *	aliaslist	The address of the first item in the list of
731  *			aliases that is to be freed
732  *
733  *  Returns:  void
734  */
735 
736 static	void
737 freealiaslist(char **aliaslist)		/* Ptr to new device list */
738 {
739 	/* Automatic Data */
740 	char   **pp;			/* Running pointer */
741 
742 	/* If there's a list ... */
743 	if (aliaslist) {
744 
745 	    /* For each entry in the old list, free the entry */
746 	    for (pp = aliaslist; *pp; pp++) free(*pp);
747 
748 	    /* Free the list */
749 	    free(aliaslist);
750 	}
751 }
752 
753 /*
754  *  char *getnextmatch(criteria, options)
755  *	struct srch	       *criteria
756  *	int			options
757  *
758  *  	Gets the next device in the device table that matches the criteria.
759  *	Returns the alias of that device.
760  *
761  *  Arguments:
762  *	criteria	The linked list of criteria to use to match a device
763  *	options		Options modifying the criteria (only one that's really
764  *			important is the DTAB_ANDCRITERIA flag)
765  *
766  *  Returns:  char *
767  *	A pointer to a malloc()ed string containing the alias of the next
768  *	device that matches the criteria, or (char *) NULL if none.
769  */
770 
771 static	char   *
772 getnextmatch(struct srch *criteria, int options)
773 {
774 	/* Automatic data */
775 	struct devtabent	*devtabent;	/* Ptr to current record */
776 	char			*alias;		/* Alias of device found */
777 	int			notdone;	/* Flag, done yet? */
778 	int			noerror;	/* Flag, had an error yet? */
779 
780 
781 	/*
782 	 *  Initializations:
783 	 *    -	No alias yet
784 	 *    - Not finished yet
785 	 *    -	Make sure there are criteria we're to use
786 	 */
787 
788 	alias = NULL;
789 	notdone = TRUE;
790 	noerror = TRUE;
791 
792 	/*  If we're to "and" the criteria...  */
793 	if (options & DTAB_ANDCRITERIA) {
794 
795 	/*
796 	 *  Search the device table until we've got a record that matches
797 	 *  all of the criteria or we run out of records
798 	 */
799 
800 	    while (notdone && (devtabent = _getdevtabent())) {
801 		if (!devtabent->comment) {
802 		    if (!criteria || matchallcriteria(devtabent, criteria)) {
803 			if (alias = malloc(strlen(devtabent->alias)+1))
804 			    (void) strcpy(alias, devtabent->alias);
805 			else noerror = FALSE;
806 			notdone = FALSE;
807 		    }
808 		}
809 		_freedevtabent(devtabent);
810 	    }
811 	} else {
812 
813 	/*
814 	 *  Search the device table until we've got a record that matches
815 	 *  any of the criteria or we run out of records
816 	 */
817 
818 	    while (notdone && (devtabent = _getdevtabent())) {
819 		if (!devtabent->comment) {
820 		    if (!criteria || matchanycriteria(devtabent, criteria)) {
821 			if (alias = malloc(strlen(devtabent->alias)+1))
822 			    (void) strcpy(alias, devtabent->alias);
823 			else noerror = FALSE;
824 			notdone = FALSE;
825 		    }
826 		}
827 		_freedevtabent(devtabent);
828 	    }
829 	}
830 
831 
832 	/* Return pointer to extracted alias (or NULL if none) */
833 	if ((alias == NULL) && noerror) errno = ENOENT;
834 	return (alias);
835 }
836 
837 /*
838  * int matchallcriteria(devtabent, criteria)
839  *
840  *	This function examines the record contained in "devtabent" and
841  *	determines if that record meets all of the criteria specified by
842  *	"criteria".
843  *
844  * Arguments:
845  *	struct devtabent *devtabent	The device table entry to examine.
846  *	struct srch    *criteria	The criteria to match.
847  *
848  * Returns:	int
849  *	Returns TRUE if the record matches criteria, FALSE otherwise.
850  */
851 
852 static	int
853 matchallcriteria(
854 	struct devtabent	*ent,		/* Entry to check */
855 	struct srch		*criteria)	/* Criteria governing match */
856 {
857 	/* Automatic data */
858 	struct srch    *p;		/* Pointer to current criteria */
859 	struct attrval *q;		/* Pointer to current attr/val pair */
860 	int		notfound;	/* TRUE if attr found in list */
861 	int		failed;		/* TRUE if record failed to match */
862 
863 
864 	/* Test only if there's criteria to test against */
865 	if (criteria && (criteria->fcn != ENDLIST)) {
866 
867 	    failed = FALSE;
868 	    for (p = criteria; !failed && (p->fcn != ENDLIST); p++) {
869 
870 		/*
871 		 * Don't compare against this criteria if it's function is
872 		 * "IGNORE"
873 		 */
874 		if (p->fcn != IGNORE) {
875 		    if (p->fcn != NOEXISTS) {
876 
877 			/*  Alias?  */
878 			if (strcmp(p->name, DTAB_ALIAS) == 0)
879 			    failed = !matches(ent->alias, p->cmp, p->fcn);
880 
881 			/*  Char special device?  */
882 			else if (strcmp(p->name, DTAB_CDEVICE) == 0)
883 			    failed = !matches(ent->cdevice, p->cmp, p->fcn);
884 
885 			/*  Block special device?  */
886 			else if (strcmp(p->name, DTAB_BDEVICE) == 0)
887 			    failed = !matches(ent->bdevice, p->cmp, p->fcn);
888 
889 			/*  Pathname?  */
890 			else if (strcmp(p->name, DTAB_PATHNAME) == 0)
891 			    failed = !matches(ent->pathname, p->cmp, p->fcn);
892 
893 			/*  Check other attributes...  */
894 			else {
895 			    notfound = TRUE;
896 			    q = ent->attrlist;
897 			    while (notfound && q) {
898 				if (strcmp(p->name, q->attr) == 0) {
899 				    notfound = FALSE;
900 				    if (!matches(q->val, p->cmp, p->fcn))
901 					failed = TRUE;
902 				} else q = q->next;
903 			    }
904 			    if (notfound) failed = TRUE;
905 			}
906 		    } else {
907 			if (strcmp(p->name, DTAB_ALIAS) == 0) failed = TRUE;
908 			else if (strcmp(p->name, DTAB_CDEVICE) == 0)
909 				failed = FALSE;
910 			else if (strcmp(p->name, DTAB_BDEVICE) == 0)
911 				failed = FALSE;
912 			else if (strcmp(p->name, DTAB_PATHNAME) == 0)
913 				failed = FALSE;
914 			else {
915 			    q = ent->attrlist;
916 			    while (!failed && q) {
917 				if (strcmp(p->name, q->attr) == 0)
918 					failed = TRUE;
919 				else q = q->next;
920 			    }
921 			}
922 		    }
923 
924 		}  /* Search function is not "IGNORE" */
925 
926 	    }  /* for loop, checking each criteria */
927 
928 	}  /* if (criteria) */
929 
930 	else failed = FALSE;  /* No criteria specified, it's a match */
931 
932 
933 	/* Return a value indicating if the record matches all criteria */
934 	return (!failed);
935 }
936 
937 /*
938  * int matchanycriteria(devtabent, criteria)
939  *
940  *	This function examines the record contained in "devtabent" and
941  *	determines if that record meets any of the criteria specified by
942  *	"criteria".
943  *
944  * Arguments:
945  *	struct devtabent *devtabent	The device table entry to examine.
946  *	struct srch      *criteria	The criteria to match.
947  *
948  * Returns:	int
949  *	Returns TRUE if the record matches criteria, FALSE otherwise.
950  */
951 
952 static	int
953 matchanycriteria(
954 	struct devtabent	*ent,		/* Entry to check */
955 	struct srch		*criteria)	/* Criteria governing match */
956 {
957 	/* Automatic data */
958 	struct srch    *p;		/* Pointer to current criteria */
959 	struct attrval *q;		/* Pointer to current attr/val pair */
960 	int		matched;	/* FLAG: TRUE if record matched */
961 	int		found;		/* FLAG: TRUE if attribute found */
962 
963 
964 	/* Test only if there's criteria to test against */
965 	if (criteria && (criteria->fcn != ENDLIST)) {
966 
967 	    matched = FALSE;
968 	    for (p = criteria; !matched && (p->fcn != ENDLIST); p++) {
969 
970 		/*
971 		 * Don't compare against this criteria if it's function is
972 		 * "IGNORE"
973 		 */
974 		if (p->fcn != IGNORE) {
975 		    if (p->fcn != NOEXISTS) {
976 
977 			/*  Alias?  */
978 			if (strcmp(p->name, DTAB_ALIAS) == 0)
979 			    matched = matches(ent->alias, p->cmp, p->fcn);
980 
981 			/*  Char special device?  */
982 			else if (strcmp(p->name, DTAB_CDEVICE) == 0)
983 			    matched = matches(ent->cdevice, p->cmp, p->fcn);
984 
985 			/*  Block special device?  */
986 			else if (strcmp(p->name, DTAB_BDEVICE) == 0)
987 			    matched = matches(ent->bdevice, p->cmp, p->fcn);
988 
989 			/*  Pathname?  */
990 			else if (strcmp(p->name, DTAB_PATHNAME) == 0)
991 			    matched = matches(ent->pathname, p->cmp, p->fcn);
992 
993 			/*  Check other attributes...  */
994 			else {
995 			    q = ent->attrlist;
996 			    found = FALSE;
997 			    while (!found && q)
998 				if (strcmp(p->name, q->attr) == 0) {
999 				    matched = matches(q->val, p->cmp, p->fcn);
1000 				    found = TRUE;
1001 				} else q = q->next;
1002 			}
1003 		    } else {
1004 			if (strcmp(p->name, DTAB_ALIAS) == 0) matched = FALSE;
1005 			else if (strcmp(p->name, DTAB_CDEVICE) == 0)
1006 				matched = FALSE;
1007 			else if (strcmp(p->name, DTAB_BDEVICE) == 0)
1008 				matched = FALSE;
1009 			else if (strcmp(p->name, DTAB_PATHNAME) == 0)
1010 				matched = FALSE;
1011 			else {
1012 			    q = ent->attrlist;
1013 			    matched = TRUE;
1014 			    while (matched && q) {
1015 				if (strcmp(p->name, q->attr) == 0)
1016 					matched = FALSE;
1017 				else q = q->next;
1018 			    }
1019 			}
1020 		    }
1021 		}  /* Search function is not "IGNORE" */
1022 
1023 	    }  /* for loop, checking each criteria */
1024 
1025 	}  /* if (criteria) */
1026 
1027 	else matched = TRUE;  /* No criteria specified, it's a match */
1028 
1029 
1030 	/* Return a value indicating if the record matches all criteria */
1031 	return (matched);
1032 }
1033 
1034 /*
1035  *  int matches(value, compare, function)
1036  *	char   *value
1037  *	char   *compare
1038  *	int	function
1039  *
1040  *	This function sees if the operation <function> is satisfied by
1041  *	comparing the value <value> with <compare>.  It returns TRUE
1042  *	if so, FALSE otherwise.
1043  *
1044  *  Arguments:
1045  *	value		Value to compare
1046  *	compare		Value to compare against
1047  *	function	Function to be satisfied
1048  *
1049  *  Returns:  int
1050  *	TRUE if the function is satisfied, FALSE otherwise
1051  */
1052 
1053 static	int
1054 matches(char *value, char *compare, int function)
1055 {
1056 	/*  Automatic data  */
1057 	int	rtn;		/* Value to return */
1058 
1059 
1060 	if (value == NULL)
1061 		value = "";
1062 
1063 	/* Do case depending on the function */
1064 	switch (function) {
1065 
1066 	/* attr=val */
1067 	case EQUAL:
1068 	    rtn = (strcmp(value, compare) == 0);
1069 	    break;
1070 
1071 	/* attr!=val */
1072 	case NOTEQUAL:
1073 	    rtn = (strcmp(value, compare) != 0);
1074 	    break;
1075 
1076 	/* attr:* */
1077 	case EXISTS:
1078 	    rtn = TRUE;
1079 	    break;
1080 
1081 	/* attr!:* */
1082 	case NOEXISTS:
1083 	    rtn = FALSE;
1084 	    break;
1085 
1086 	/* Shouldn't get here... */
1087 	default:
1088 	    rtn = FALSE;
1089 	    break;
1090 	}
1091 
1092 	/* Return a value indicating if the match was made */
1093 	return (rtn);
1094 }
1095