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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <regex.h>
30#include <locale.h>
31#include <langinfo.h>
32#include <limits.h>
33#include <errno.h>
34#include "getresponse.h"
35
36/* defaults - C locale values for yesstr, nostr, yesexpr (LC_MESSAGES) */
37#define	DEFAULT_YESSTR  "yes"
38#define	DEFAULT_NOSTR   "no"
39#define	DEFAULT_YESEXPR "^[yY]"
40#define	DEFAULT_NOEXPR	"^[nN]"
41
42#define	FREE_MEM        \
43		free(yesstr);   \
44		free(nostr);    \
45		free(yesexpr);  \
46		free(noexpr)
47
48#define	SET_DEFAULT_STRS \
49	yesstr = DEFAULT_YESSTR; \
50	nostr = DEFAULT_NOSTR; \
51	yesexpr = DEFAULT_YESEXPR; \
52	noexpr = DEFAULT_NOEXPR;
53
54/* variables used by getresponse functions */
55char    *yesstr = NULL;
56char    *nostr = NULL;
57
58/* for regcomp()/regexec() yesexpr and noexpr */
59static regex_t preg_yes, preg_no;
60
61/*
62 * This function compiles a regular expression that is used to match an
63 * affirmative response from the user, and also assigns the strings used
64 * in the prompts that request affirmative or negative responses.  The
65 * locale's values for YESEXPR, NOEXPR, YESSTR and NOSTR are used.
66 *
67 * If there are any problems using the locale's YESEXPR, NOEXPR, YESSTR or NOSTR
68 * values, default values of YESEXPR, YESSTR and NOSTR will be used
69 * as a fallback.  The default values are the same as the C locale values.
70 */
71int
72init_yes(void)
73{
74	int	fallback = 0;
75	char    *yesexpr;
76	char	*noexpr;
77
78	/* get yes expression and strings for yes/no prompts */
79	yesstr  = strdup(nl_langinfo(YESSTR));
80	nostr   = strdup(nl_langinfo(NOSTR));
81	yesexpr = strdup(nl_langinfo(YESEXPR));
82	noexpr  = strdup(nl_langinfo(NOEXPR));
83
84	if (yesstr == NULL || nostr == NULL ||
85	    yesexpr == NULL || noexpr == NULL) {
86		FREE_MEM;
87		errno = ENOMEM;
88		return (-1);
89	}
90
91	/* if problem with locale strings, use default values */
92	if (*yesstr == '\0' || *nostr == '\0' ||
93	    *yesexpr == '\0' || *noexpr == '\0') {
94		FREE_MEM;
95		SET_DEFAULT_STRS;
96		fallback = 1;
97	}
98	/* Compile the yes and no expressions */
99	while (regcomp(&preg_yes, yesexpr, REG_EXTENDED | REG_NOSUB) != 0 ||
100	    regcomp(&preg_no, noexpr, REG_EXTENDED | REG_NOSUB) != 0) {
101		if (fallback == 1) {
102			/* The fallback yesexpr failed, so exit */
103			errno = EINVAL;
104			return (-1);
105		}
106		/* The locale's yesexpr or noexpr failed so use fallback */
107		FREE_MEM;
108		SET_DEFAULT_STRS;
109		fallback = 1;
110	}
111	if (fallback == 0) {
112		free(yesexpr);
113		free(noexpr);
114	}
115	return (0);
116}
117
118void
119fini_yes(void)
120{
121	free(yesstr);
122	free(nostr);
123	yesstr = DEFAULT_YESSTR;
124	nostr = DEFAULT_NOSTR;
125	regfree(&preg_yes);
126	regfree(&preg_no);
127}
128
129static int
130yes_no(int (*func)(char *))
131{
132	int	i, b;
133	char    ans[LINE_MAX + 1];
134
135	/* Get user's answer */
136	i = 0;
137	for (;;) {
138		b = getchar();
139		if (b == '\n' || b == '\0' || b == EOF)
140			break;
141		if (i < LINE_MAX)
142			ans[i] = b;
143		i++;
144	}
145	if (i >= LINE_MAX)
146		ans[LINE_MAX] = '\0';
147	else
148		ans[i] = '\0';
149
150	return (func(ans));
151}
152
153static int
154yes_no_check(char *ans, regex_t *reg1, regex_t *reg2)
155{
156	if (regexec(reg1, ans, 0, NULL, 0) == 0) {
157		if (regexec(reg2, ans, 0, NULL, 0) == 0) {
158			/* Both Expressions Match (reg2 conservative) */
159			return (0);
160		}
161		/* Match */
162		return (1);
163	}
164	return (0);
165}
166
167/*
168 * yes_check() returns 1 if the input string is matched by yesexpr and is
169 * not matched by noexpr;  otherwise yes_check() returns 0.
170 */
171int
172yes_check(char *ans)
173{
174	return (yes_no_check(ans, &preg_yes, &preg_no));
175}
176
177/*
178 * no_check() returns 1 if the input string is matched by noexpr and is
179 * not matched by yesexpr;  otherwise no_check() returns 0.
180 */
181int
182no_check(char *ans)
183{
184	return (yes_no_check(ans, &preg_no, &preg_yes));
185}
186
187int
188yes(void)
189{
190	return (yes_no(yes_check));
191}
192
193int
194no(void)
195{
196	return (yes_no(no_check));
197}
198