xref: /illumos-gate/usr/src/common/util/getresponse.c (revision b6ed8f22)
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 */
55 char    *yesstr = NULL;
56 char    *nostr = NULL;
57 
58 /* for regcomp()/regexec() yesexpr and noexpr */
59 static 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  */
71 int
init_yes(void)72 init_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 
118 void
fini_yes(void)119 fini_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 
129 static int
yes_no(int (* func)(char *))130 yes_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 
153 static int
yes_no_check(char * ans,regex_t * reg1,regex_t * reg2)154 yes_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  */
171 int
yes_check(char * ans)172 yes_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  */
181 int
no_check(char * ans)182 no_check(char *ans)
183 {
184 	return (yes_no_check(ans, &preg_no, &preg_yes));
185 }
186 
187 int
yes(void)188 yes(void)
189 {
190 	return (yes_no(yes_check));
191 }
192 
193 int
no(void)194 no(void)
195 {
196 	return (yes_no(no_check));
197 }
198