xref: /illumos-gate/usr/src/common/util/getresponse.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 */
62 char    *yesstr = NULL;
63 char    *nostr = NULL;
64 
65 /* for regcomp()/regexec() yesexpr and noexpr */
66 static 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  */
78 int
79 init_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 
121 static int
122 yes_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 
142 static int
143 yes_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  */
160 int
161 yes_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  */
170 int
171 no_check(char *ans)
172 {
173 	return (yes_no_check(ans, &preg_no, &preg_yes));
174 }
175 
176 int
177 yes(void)
178 {
179 	return (yes_no(yes_check));
180 }
181 
182 int
183 no(void)
184 {
185 	return (yes_no(no_check));
186 }
187