xref: /openbsd-src/sys/ddb/db_hangman.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: db_hangman.c,v 1.30 2014/07/08 13:02:57 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 Theo de Raadt, Michael Shalayeff
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 
32 #include <machine/db_machdep.h>
33 
34 #include <ddb/db_sym.h>
35 #include <ddb/db_extern.h>
36 #include <ddb/db_output.h>
37 
38 #include <dev/cons.h>
39 #include <dev/rndvar.h>
40 
41 #define ABC_ISCLR(c)	sabc->abc[(c)-'a']==0
42 #define ABC_ISWRONG(c)	sabc->abc[(c)-'a']=='_'
43 #define ABC_SETWRONG(c)		(sabc->abc[(c)-'a']='_')
44 #define ABC_SETRIGHT(c)		(sabc->abc[(c)-'a']='+')
45 #define ABC_CLR()	memset(sabc->abc,0,sizeof sabc->abc)
46 struct _abc {
47 	char	abc[26+2];	/* for int32 alignment */
48 };
49 
50 #define	TOLOWER(c)	((c)|0x20)
51 #define	ISLOWALPHA(c)	('a'<=(c) && (c)<='z')
52 #define	ISALPHA(c)	ISLOWALPHA(TOLOWER(c))
53 
54 void	 db_hang(int, char *, struct _abc *);
55 
56 u_long		db_plays, db_guesses;
57 
58 static const char hangpic[]=
59 	"\n88888\r\n"
60 	"9 7 6\r\n"
61 	"97  5\r\n"
62 	"9  423\r\n"
63 	"9   2\r\n"
64 	"9  1 0\r\n"
65 	"9\r\n"
66 	"9  ";
67 static const char substchar[]="\\/|\\/O|/-|";
68 
69 struct db_hang_forall_arg {
70 	int cnt;
71 	db_sym_t sym;
72 };
73 
74 /*
75  * Horrible abuse of the forall function, but we're not in a hurry.
76  */
77 static void db_hang_forall(db_symtab_t *, db_sym_t, char *, char *, int,
78 			void *);
79 
80 static void
81 db_hang_forall(db_symtab_t *stab, db_sym_t sym, char *name, char *suff, int pre,
82     void *varg)
83 {
84 	struct db_hang_forall_arg *arg = (struct db_hang_forall_arg *)varg;
85 
86 	if (arg->cnt-- == 0)
87 		arg->sym = sym;
88 }
89 
90 static __inline char *
91 db_randomsym(size_t *lenp)
92 {
93 	extern db_symtab_t db_symtabs[];
94 	db_symtab_t *stab;
95 	int nsymtabs, nsyms;
96 	char	*p, *q;
97 	struct db_hang_forall_arg dfa;
98 
99 	for (nsymtabs = 0; db_symtabs[nsymtabs].name != NULL; nsymtabs++)
100 		;
101 
102 	if (nsymtabs == 0)
103 		return (NULL);
104 
105 	stab = &db_symtabs[arc4random_uniform(nsymtabs)];
106 
107 	dfa.cnt = 0;
108 	X_db_forall(stab, db_hang_forall, &dfa);
109 	nsyms = -dfa.cnt;
110 
111 	if (nsyms == 0)
112 		return (NULL);
113 
114 	dfa.cnt = arc4random_uniform(nsyms);
115 	X_db_forall(stab, db_hang_forall, &dfa);
116 
117 	q = db_qualify(dfa.sym, stab->name);
118 
119 	/* don't show symtab name if there are less than 3 of 'em */
120 	if (nsymtabs < 3)
121 		while (*q++ != ':');
122 
123 	/* strlen(q) && ignoring underscores and colons */
124 	for ((*lenp) = 0, p = q; *p; p++)
125 		if (ISALPHA(*p))
126 			(*lenp)++;
127 
128 	return (q);
129 }
130 
131 void
132 db_hang(int tries, char *word, struct _abc *sabc)
133 {
134 	const char	*p;
135 	int i;
136 	int c;
137 #ifdef ABC_BITMASK
138 	int m;
139 #endif
140 
141 	for (p = hangpic; *p; p++)
142 		cnputc((*p >= '0' && *p <= '9') ? ((tries <= (*p) - '0') ?
143 		    substchar[(*p) - '0'] : ' ') : *p);
144 
145 	for (p = word; *p; p++) {
146 		c = TOLOWER(*p);
147 		cnputc(ISLOWALPHA(c) && ABC_ISCLR(c) ? '-' : *p);
148 	}
149 
150 #ifdef ABC_WRONGSTR
151 	db_printf(" (%s)\r", ABC_WRONGSTR);
152 #else
153 	db_printf(" (");
154 
155 #ifdef ABC_BITMASK
156 	m = sabc->wrong;
157 	for (i = 'a'; i <= 'z'; ++i, m >>= 1)
158 		if (m&1)
159 			cnputc(i);
160 #else
161 	for (i = 'a'; i <= 'z'; ++i)
162 		if (ABC_ISWRONG(i))
163 			cnputc(i);
164 #endif
165 
166 	db_printf(")\r");
167 #endif
168 }
169 
170 void
171 db_hangman(db_expr_t addr, int haddr, db_expr_t count, char *modif)
172 {
173 	char	*word;
174 	size_t	tries;
175 	size_t	len;
176 	struct _abc sabc[1];
177 	int	skill;
178 
179 	if (modif[0] != 's' || (skill = modif[1] - '0') > 9U)
180 		skill = 3;
181 	word = NULL;
182 	tries = 0;
183 	for (;;) {
184 
185 		if (word == NULL) {
186 			ABC_CLR();
187 
188 			tries = skill + 1;
189 			word = db_randomsym(&len);
190 			if (word == NULL)
191 				break;
192 
193 			db_plays++;
194 		}
195 
196 		{
197 			int c;
198 
199 			db_hang(tries, word, sabc);
200 			c = cngetc();
201 			c = TOLOWER(c);
202 
203 			if (ISLOWALPHA(c) && ABC_ISCLR(c)) {
204 				char	*p;
205 				size_t	n;
206 
207 					/* strchr(word,c) */
208 				for (n = 0, p = word; *p ; p++)
209 					if (TOLOWER(*p) == c)
210 						n++;
211 
212 				if (n) {
213 					ABC_SETRIGHT(c);
214 					len -= n;
215 				} else {
216 					ABC_SETWRONG(c);
217 					tries--;
218 				}
219 			}
220 		}
221 
222 		if (tries && len)
223 			continue;
224 
225 		if (!tries && skill > 2) {
226 			char	*p = word;
227 			for (; *p; p++)
228 				if (ISALPHA(*p))
229 					ABC_SETRIGHT(TOLOWER(*p));
230 		}
231 		if (tries)
232 			db_guesses++;
233 		db_hang(tries, word, sabc);
234 		db_printf("\nScore: %lu/%lu\n", db_plays, db_guesses);
235 		word = NULL;
236 		if (tries)
237 			break;
238 	}
239 }
240