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) 1988 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.2	*/
32
33/*LINTLIBRARY*/
34
35#include <sys/types.h>
36#include <stdlib.h>
37#include "utility.h"
38
39/*
40 *	TYPE_ENUM standard type
41 *
42 *	usage:
43 *		set_field_type(f, TYPE_ENUM, list, checkcase, checkuniq);
44 *
45 *		char ** list;	list of acceptable strings
46 *		int checkcase;	TRUE - upper/lower case is significant
47 *		int checkuniq;	TRUE - unique match required
48 *
49 */
50typedef struct {
51
52	char **	list;
53	int	checkcase;
54	int	checkuniq;
55	int	count;
56} ENUM;
57
58static char * make_enum(va_list *);
59static char * copy_enum(char *);
60static void free_enum(char *);
61static int fcheck_enum(FIELD *, char *);
62static int next_enum(FIELD *, char *);
63static int prev_enum(FIELD *, char *);
64
65static FIELDTYPE typeENUM =
66{
67				ARGS | CHOICE,		/* status	*/
68				1,			/* ref		*/
69				(FIELDTYPE *) 0,	/* left		*/
70				(FIELDTYPE *) 0,	/* right	*/
71				make_enum,		/* makearg	*/
72				copy_enum,		/* copyarg	*/
73				free_enum,		/* freearg	*/
74				fcheck_enum,		/* fcheck	*/
75				(PTF_int) 0,		/* ccheck	*/
76				next_enum,		/* next		*/
77				prev_enum,		/* prev		*/
78};
79
80FIELDTYPE * TYPE_ENUM = &typeENUM;
81
82static char *
83make_enum(va_list *ap)
84{
85	ENUM * n;
86
87	if (Alloc(n, ENUM)) {
88		char **		v;
89
90		n -> list	= va_arg(*ap, char **);
91		n -> checkcase	= va_arg(*ap, int);
92		n -> checkuniq	= va_arg(*ap, int);
93
94		for (v = n -> list; *v; ++v)
95			;
96		n -> count = (int) (v - n -> list);
97	}
98	return ((char *) n);
99}
100
101static char *
102copy_enum(char *arg)
103{
104	ENUM * n;
105
106	if (Alloc(n, ENUM))
107		*n = *((ENUM *) arg);
108	return ((char *) n);
109}
110
111static void
112free_enum(char *arg)
113{
114	Free(arg);
115}
116
117#define	NO_MATCH		0
118#define	PARTIAL_MATCH		1
119#define	EXACT_MATCH		2
120
121static int
122cmp(char *x, char *v, int checkcase)
123{
124	while (*v && *v == ' ')			/* remove leading blanks */
125		++v;
126	while (*x && *x == ' ')			/* remove leading blanks */
127		++x;
128
129	if (*v == '\0')
130		return (*x == '\0' ? EXACT_MATCH : NO_MATCH);
131
132	if (checkcase) {			/* case is significant */
133		while (*x++ == *v)
134			if (*v++ == '\0')
135				return (EXACT_MATCH);
136	} else {				/* ignore case */
137		while (toupper (*x++) == toupper (*v))
138			if (*v++ == '\0')
139				return (EXACT_MATCH);
140	}
141	while (*v && *v == ' ')			/* remove trailing blanks */
142		++v;
143	if (*v)
144		return (NO_MATCH);
145	else
146		return (*--x ? PARTIAL_MATCH : EXACT_MATCH);
147}
148
149static int
150fcheck_enum(FIELD *f, char *arg)
151{
152	ENUM *		n		= (ENUM *) arg;
153	char **		list		= n -> list;
154	int		checkcase	= n -> checkcase;
155	int		checkuniq	= n -> checkuniq;
156	int		m;
157	char *		v		= field_buffer(f, 0);
158	char *		x;
159
160	while (x = *list++)
161		if (m = cmp(x, v, checkcase)) {
162			char * value = x;
163
164			if (checkuniq && m != EXACT_MATCH)
165				while (x = *list++)
166					if (m = cmp(x, v, checkcase)) {
167						if (m == EXACT_MATCH) {
168							value = x;
169							break;
170						}
171						else
172							value = (char *) 0;
173					}
174			if (! value)
175				return (FALSE);
176
177			(void) set_field_buffer(f, 0, value);
178			return (TRUE);
179		}
180
181	return (FALSE);
182}
183
184static int
185next_enum(FIELD *f, char *arg)
186{
187	ENUM *		n		= (ENUM *) arg;
188	char **		list		= n -> list;
189	int		checkcase	= n -> checkcase;
190	int		count		= n -> count;
191	char *		v		= field_buffer(f, 0);
192
193	while (count--)
194		if (cmp(*list++, v, checkcase) == EXACT_MATCH)
195			break;
196	if (count <= 0)
197		list = n -> list;
198
199	if (count >= 0 || cmp("", v, checkcase) == EXACT_MATCH) {
200		(void) set_field_buffer(f, 0, *list);
201		return (TRUE);
202	}
203	return (FALSE);
204}
205
206static int
207prev_enum(FIELD *f, char *arg)
208{
209	ENUM *		n		= (ENUM *) arg;
210	char **		list		= n -> list + n -> count - 1;
211	int		checkcase	= n -> checkcase;
212	int		count		= n -> count;
213	char *		v		= field_buffer(f, 0);
214
215	while (count--)
216		if (cmp(*list--, v, checkcase) == EXACT_MATCH)
217			break;
218	if (count <= 0)
219		list = n -> list + n -> count - 1;
220
221	if (count >= 0 || cmp("", v, checkcase) == EXACT_MATCH) {
222		(void) set_field_buffer(f, 0, *list);
223		return (TRUE);
224	}
225	return (FALSE);
226}
227