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