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#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28
29#include <stdio.h>
30#include <string.h>
31#include <stdlib.h>
32#include <regex.h>
33#include <locale.h>
34#include <langinfo.h>
35#include <limits.h>
36#include <errno.h>
37#include "getresponse.h"
38
39/* defaults - C locale values for yesstr, nostr, yesexpr (LC_MESSAGES) */
40#define	DEFAULT_YESSTR  "yes"
41#define	DEFAULT_NOSTR   "no"
42#define	DEFAULT_YESEXPR "^[yY]"
43#define	DEFAULT_NOEXPR	"^[nN]"
44
45#define	FREE_MEM        \
46	if (yesstr)     \
47		free(yesstr);   \
48	if (nostr)      \
49		free(nostr);    \
50	if (yesexpr)    \
51		free(yesexpr);  \
52	if (noexpr)     \
53		free(noexpr)
54
55#define	SET_DEFAULT_STRS \
56	yesstr = DEFAULT_YESSTR; \
57	nostr = DEFAULT_NOSTR; \
58	yesexpr = DEFAULT_YESEXPR; \
59	noexpr = DEFAULT_NOEXPR;
60
61/* variables used by getresponse functions */
62char    *yesstr = NULL;
63char    *nostr = NULL;
64
65/* for regcomp()/regexec() yesexpr and noexpr */
66static regex_t preg_yes, preg_no;
67
68/*
69 * This function compiles a regular expression that is used to match an
70 * affirmative response from the user, and also assigns the strings used
71 * in the prompts that request affirmative or negative responses.  The
72 * locale's values for YESEXPR, NOEXPR, YESSTR and NOSTR are used.
73 *
74 * If there are any problems using the locale's YESEXPR, NOEXPR, YESSTR or NOSTR
75 * values, default values of YESEXPR, YESSTR and NOSTR will be used
76 * as a fallback.  The default values are the same as the C locale values.
77 */
78int
79init_yes(void)
80{
81	int	fallback = 0;
82	char    *yesexpr;
83	char	*noexpr;
84
85	/* get yes expression and strings for yes/no prompts */
86	yesstr  = strdup(nl_langinfo(YESSTR));
87	nostr   = strdup(nl_langinfo(NOSTR));
88	yesexpr = strdup(nl_langinfo(YESEXPR));
89	noexpr  = strdup(nl_langinfo(NOEXPR));
90
91	if (yesstr == NULL || nostr == NULL ||
92	    yesexpr == NULL || noexpr == NULL) {
93		FREE_MEM;
94		errno = ENOMEM;
95		return (-1);
96	}
97
98	/* if problem with locale strings, use default values */
99	if (*yesstr == '\0' || *nostr == '\0' ||
100	    *yesexpr == '\0' || *noexpr == '\0') {
101		FREE_MEM;
102		SET_DEFAULT_STRS;
103		fallback = 1;
104	}
105	/* Compile the yes and no expressions */
106	while (regcomp(&preg_yes, yesexpr, REG_EXTENDED | REG_NOSUB) != 0 ||
107	    regcomp(&preg_no, noexpr, REG_EXTENDED | REG_NOSUB) != 0) {
108		if (fallback == 1) {
109			/* The fallback yesexpr failed, so exit */
110			errno = EINVAL;
111			return (-1);
112		}
113		/* The locale's yesexpr or noexpr failed so use fallback */
114		FREE_MEM;
115		SET_DEFAULT_STRS;
116		fallback = 1;
117	}
118	return (0);
119}
120
121static int
122yes_no(int (*func)(char *))
123{
124	int	i, b;
125	char    ans[LINE_MAX + 1];
126
127	/* Get user's answer */
128	for (i = 0; b = getchar(); i++) {
129		if (b == '\n' || b == '\0' || b == EOF)
130			break;
131		if (i < LINE_MAX)
132			ans[i] = b;
133	}
134	if (i >= LINE_MAX)
135		ans[LINE_MAX] = '\0';
136	else
137		ans[i] = '\0';
138
139	return (func(ans));
140}
141
142static int
143yes_no_check(char *ans, regex_t *reg1, regex_t *reg2)
144{
145	if (regexec(reg1, ans, 0, NULL, 0) == 0) {
146		if (regexec(reg2, ans, 0, NULL, 0) == 0) {
147			/* Both Expressions Match (reg2 conservative) */
148			return (0);
149		}
150		/* Match */
151		return (1);
152	}
153	return (0);
154}
155
156/*
157 * yes_check() returns 1 if the input string is matched by yesexpr and is
158 * not matched by noexpr;  otherwise yes_check() returns 0.
159 */
160int
161yes_check(char *ans)
162{
163	return (yes_no_check(ans, &preg_yes, &preg_no));
164}
165
166/*
167 * no_check() returns 1 if the input string is matched by noexpr and is
168 * not matched by yesexpr;  otherwise no_check() returns 0.
169 */
170int
171no_check(char *ans)
172{
173	return (yes_no_check(ans, &preg_no, &preg_yes));
174}
175
176int
177yes(void)
178{
179	return (yes_no(yes_check));
180}
181
182int
183no(void)
184{
185	return (yes_no(no_check));
186}
187