xref: /haiku/src/add-ons/kernel/debugger/hangman/hangman.c (revision 020cbad9d40235a2c50a81a42d69912a5ff8fbc4)
1 #ifdef _KERNEL_MODE
2 #include <Drivers.h>
3 #include <KernelExport.h>
4 #include <module.h>
5 #endif
6 #include <OS.h>
7 #include <image.h>
8 #include <ctype.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 
15 /* as driver or module */
16 //#define AS_DRIVER 1
17 
18 /* do we reboot on loose ? */
19 //#define FAIL_IN_BSOD_CAUSE_REBOOT 1
20 
21 /* shortcut to be able to exit (and take a screenshot) */
22 #define CAN_EXIT_ON_DASH
23 
24 #define MAX_FAILS_BEFORE_BSOD 0
25 
26 #ifdef __HAIKU__
27 #define FORTUNE_FILE "/etc/fortunes/Fortunes"
28 #else
29 #define FORTUNE_FILE "/etc/fortunes/default"
30 #endif
31 
32 #define KCMD_HELP "A funny KDL hangman game :-)"
33 
34 #define DEV_ENTRY "misc/hangman"
35 
36 #define KERNEL_IMAGE_ID 1
37 
38 #define MIN_LETTERS 3
39 #define MAX_LETTERS 10
40 
41 #define MAX_CACHED_WORDS 5
42 
43 char words[MAX_CACHED_WORDS][MAX_LETTERS+1];
44 
45 #ifndef __HAIKU__
46 
47 /* design ripped off from http://www.latms.berkeley.k12.ca.us/perl/node30.html :) */
48 static char hungman[] = \
49 "  ____      \n" \
50 "  |   |     \n" \
51 "  |   %c     \n" \
52 "  |  %c%c%c    \n" \
53 "  |  %c %c    \n" \
54 "  |         \n";
55 
56 #else
57 
58 /* some colors */
59 static char hungman_ansi[] = \
60 "  ____      \n" \
61 "  |   |     \n" \
62 "  |   \033[36m%c\033[0m     \n" \
63 "  |  \033[35m%c%c%c\033[0m    \n" \
64 "  |  \033[35m%c %c\033[0m    \n" \
65 "  |         \n";
66 
67 #endif
68 
69 // for gets,
70 #define BIGBUFFSZ 128
71 char bigbuffer[BIGBUFFSZ];
72 
73 #define BIT_FROM_LETTER(l) (0x1 << (l - 'a'))
74 
75 status_t init_words(char *from);
76 void print_hangman(int fails);
77 void display_word(int current, uint32 tried_letters);
78 int play_hangman(void);
79 int kdlhangman(int argc, char **argv);
80 
81 #ifdef _KERNEL_MODE
82 
83 # ifdef __HAIKU__
84 extern int kgets(char *buf, int len);
85 #  define PRINTF kprintf
86 #  define GETS(a) ({int l; kprintf("hangman> "); l = kgets(a, sizeof(a)); l?a:NULL;})
87 #  define HIDDEN_LETTER '_'
88 #  define HUNGMAN hungman_ansi
89 # else
90 /* BeOS R5 version, needs some R5 kernel privates... */
91 /* the kernel pointer to the bsod_gets */
92 static char *(*bsod_gets)(char *, char *, int);
93 extern char *(*bsod_kgets)(char *, char *, int);
94 //extern char *bsod_gets(char *);
95 /* saved here before panic()ing */
96 char *(*bsod_saved_kgets)(char *, char *, int);
97 #  define PRINTF kprintf
98 #  define GETS(a) ((*bsod_kgets)?(*bsod_kgets):(*bsod_gets))("hangman> ", a, sizeof(a))
99 #  define HIDDEN_LETTER '_'
100 #  define HUNGMAN hungman
101 # endif
102 #else
103 /* userland version */
104 # define PRINTF printf
105 # define GETS(a) gets(a)
106 # define dprintf printf
107 # define HIDDEN_LETTER '.'
108 #  define HUNGMAN hungman_ansi
109 #endif /* !_KERNEL_MODE */
110 
111 
112 status_t
113 init_words(char *from)
114 {
115 	int fd;
116 	size_t sz, got;
117 	int current, beg, end, i;
118 	struct stat st;
119 
120 	memset((void *)words, 0, sizeof(words));
121 	fd = open(from, O_RDONLY);
122 	if (fd < B_OK)
123 		return fd;
124 	/* lseek() seems to always return 0 from the kernel ??? */
125 	if (fstat(fd, &st))
126 		return B_ERROR;
127 	sz = (size_t)st.st_size;
128 //	sz = (size_t)lseek(fd, 0, SEEK_END);
129 //	dprintf("khangman: lseek(): %ld\n", sz);
130 	if (sz < 30) {
131 		dprintf("hangman: fortune file too small\n");
132 		return B_ERROR;
133 	}
134 //	lseek(fd, 0, SEEK_SET);
135 	//srand((unsigned int)(system_time() + (system_time() >> 32) + find_thread(NULL)));
136 	srand((unsigned int)(system_time() & 0x0ffffffff));
137 	for (current = 0; current < MAX_CACHED_WORDS; current++) {
138 		off_t offset = (rand() % (sz - MAX_LETTERS));
139 	//	dprintf("current %d, offset %ld\n", current, (long)offset);
140 		lseek(fd, offset, SEEK_SET);
141 		got = read(fd, bigbuffer, BIGBUFFSZ - 2);
142 	//	dprintf("--------------buff(%d):\n%20s\n", current, bigbuffer);
143 		for (beg = 0; beg < got && isalpha(bigbuffer[beg]); beg++);
144 		for (; beg < got && !isalpha(bigbuffer[beg]); beg++);
145 		if (beg + 1 < got && isalpha(bigbuffer[beg])) {
146 			for (end = beg; end < got && isalpha(bigbuffer[end]); end++);
147 			if (end < got && !isalpha(bigbuffer[end]) && beg + MIN_LETTERS < end) {
148 				/* got one */
149 				/* tolower */
150 				for (i = beg; i < end; i++)
151 					bigbuffer[i] = tolower(bigbuffer[i]);
152 				strncpy(&(words[current][0]), &(bigbuffer[beg]), end - beg);
153 			} else
154 				current--;
155 		} else
156 			current--;
157 	}
158 	close(fd);
159 /*
160 	for (current = 0; current < MAX_CACHED_WORDS; current++)
161 		dprintf("%s\n", words[current]);
162 */
163 	return B_OK;
164 }
165 
166 
167 void
168 print_hangman(int fails)
169 {
170 	PRINTF(HUNGMAN,
171 		(fails > 0)?'O':' ',
172 		(fails > 2)?'/':' ',
173 		(fails > 1)?'|':' ',
174 		(fails > 3)?'\\':' ',
175 		(fails > 4)?'/':' ',
176 		(fails > 5)?'\\':' ');
177 }
178 
179 
180 void
181 display_word(int current, uint32 tried_letters)
182 {
183 	int i = 0;
184 	PRINTF("word> ");
185 	while (words[current][i]) {
186 		PRINTF("%c", (BIT_FROM_LETTER(words[current][i]) & tried_letters)?(words[current][i]):HIDDEN_LETTER);
187 		i++;
188 	}
189 	PRINTF("\n");
190 }
191 
192 int
193 play_hangman(void)
194 {
195 	int current;
196 	int score = 0;
197 	int bad_guesses;
198 	uint32 tried_letters;
199 	char *str;
200 	char try;
201 	int gotit, gotone;
202 
203 	for (current = 0; current < MAX_CACHED_WORDS; current++) {
204 		tried_letters = 0;
205 		gotit = 0;
206 		for (bad_guesses = 0; bad_guesses < 6; bad_guesses++) {
207 			do {
208 				gotit = 0;
209 				gotone = 1;
210 				display_word(current, tried_letters);
211 				str = GETS(bigbuffer);
212 				if (!str) {
213 					str = bigbuffer;
214 					PRINTF("buffer:%s\n", str);
215 				}
216 #ifdef CAN_EXIT_ON_DASH
217 				if (str[0] == '-') /* emergency exit */
218 					return 0;
219 #endif
220 				if (!isalpha(str[0])) {
221 					PRINTF("not a letter\n");
222 				} else {
223 					try = tolower(str[0]);
224 					if (BIT_FROM_LETTER(try) & tried_letters) {
225 						PRINTF("%c already tried\n", try);
226 					} else {
227 						// REUSE
228 						str = words[current];
229 						gotit = 1;
230 						gotone = 0;
231 						tried_letters |= BIT_FROM_LETTER(try);
232 						while (*str) {
233 							if (!(BIT_FROM_LETTER(*str) & tried_letters))
234 								gotit=0;
235 							if (*str == try) {
236 								gotone = 1;
237 							}
238 							str++;
239 						}
240 					}
241 				}
242 				//PRINTF("gotone:%d, gotit:%d, tried_letters:%08lx\n", gotone, gotit, tried_letters);
243 			} while(tried_letters != 0x03ffffff && !gotit && gotone);
244 			if (gotit)
245 				break;
246 			print_hangman(bad_guesses+1);
247 		}
248 		if (bad_guesses < 6) {
249 			display_word(current, 0x03ffffff);
250 			if (strlen(words[current]) < 5)
251 				PRINTF("That was easy :-P\n");
252 			else if (strlen(words[current]) < 7)
253 				PRINTF("Good one !\n");
254 			else
255 				PRINTF("You got this hard one ! :-)\n");
256 			score ++;
257 		}
258 /**/
259 		else return score;
260 /**/
261 	}
262 	return score;
263 }
264 
265 
266 #ifdef _KERNEL_MODE /* driver parts */
267 
268 
269 #ifndef __HAIKU__ /* BeOS intimacy revealed */
270 //char *bsod_wrapper_gets(char *p, int len)
271 //char *bsod_wrapper_gets(int len, char *p)
272 char *
273 bsod_wrapper_gets(char *prompt, char *p, int len)
274 {
275 	/* fall back to the normal gets() */
276 	bsod_kgets = bsod_saved_kgets;
277 //	if (!bsod_kgets)
278 //		bsod_kgets = bsod_gets;
279 	/* and fake some typing */
280 	strcpy(p, fake_typed);
281 	return p;
282 }
283 #else
284 
285 #endif
286 
287 
288 int
289 kdlhangman(int argc, char **argv)
290 {
291 	int score;
292 
293 	if (argc > 1 && strcmp(argv[1], "--help") == 0) {
294 		PRINTF("%s\n", KCMD_HELP);
295 		return 0;
296 	}
297 
298 	score = play_hangman();
299 PRINTF("score %d\n", score);
300 	if (score > (MAX_CACHED_WORDS - MAX_FAILS_BEFORE_BSOD)) {
301 		PRINTF("Congrats !\n");
302 	}
303 	if (score < (MAX_CACHED_WORDS - MAX_FAILS_BEFORE_BSOD)) {
304 #ifdef FAIL_IN_BSOD_CAUSE_REBOOT
305 		PRINTF("Hmmm, sorry, need to trash your hdd... Ok, just a reboot then\n");
306 		fake_typed = "reboot";
307 		bsod_kgets = bsod_wrapper_gets;
308 		return 1;
309 #else
310 		PRINTF("Hmmm, sorry, need to trash your hdd... Well, I'll be nice this time\n");
311 #endif
312 	}
313 	//return B_KDEBUG_CONT;
314 	return B_KDEBUG_QUIT;
315 }
316 
317 
318 #  ifdef AS_DRIVER
319 
320 typedef struct {
321 	int dummy;
322 } cookie_t;
323 
324 const char * device_names[]={DEV_ENTRY, NULL};
325 
326 
327 status_t
328 init_hardware(void) {
329 	return B_OK;
330 }
331 
332 
333 status_t
334 init_driver(void)
335 {
336 	status_t err;
337 
338 	err = init_words(FORTUNE_FILE);
339 	if (err < B_OK) {
340 		dprintf("hangman: error reading fortune file: %s\n", strerror(err));
341 		return B_ERROR;
342 	}
343 	get_image_symbol(KERNEL_IMAGE_ID, "bsod_gets", B_SYMBOL_TYPE_ANY, (void **)&bsod_gets);
344 	add_debugger_command("kdlhangman", kdlhangman, KCMD_HELP);
345 	return B_OK;
346 }
347 
348 
349 void
350 uninit_driver(void)
351 {
352 	remove_debugger_command("kdlhangman", kdlhangman);
353 }
354 
355 
356 const char **
357 publish_devices()
358 {
359 	return device_names;
360 }
361 
362 
363 status_t
364 khangman_open(const char *name, uint32 flags, cookie_t **cookie)
365 {
366 	(void)name; (void)flags;
367 	*cookie = (void*)malloc(sizeof(cookie_t));
368 	if (*cookie == NULL) {
369 		dprintf("khangman_open : error allocating cookie\n");
370 		goto err0;
371 	}
372 	memset(*cookie, 0, sizeof(cookie_t));
373 	return B_OK;
374 err0:
375 	return B_ERROR;
376 }
377 
378 
379 status_t
380 khangman_close(void *cookie)
381 {
382 	(void)cookie;
383 	return B_OK;
384 }
385 
386 
387 status_t
388 khangman_free(cookie_t *cookie)
389 {
390 	free(cookie);
391 	return B_OK;
392 }
393 
394 
395 status_t
396 khangman_read(cookie_t *cookie, off_t position, void *data, size_t *numbytes)
397 {
398 	*numbytes = 0;
399 	return B_NOT_ALLOWED;
400 }
401 
402 
403 status_t
404 khangman_write(void *cookie, off_t position, const void *data, size_t *numbytes)
405 {
406 	(void)cookie; (void)position; (void)data; (void)numbytes;
407 	//*numbytes = 0;
408 	/* here we get to kdlhangman */
409 	fake_typed = "kdlhangman";
410 	bsod_saved_kgets = bsod_kgets;
411 	bsod_kgets = bsod_wrapper_gets;
412 	kernel_debugger("So much more fun in KDL...");
413 
414 	return B_OK;
415 }
416 
417 
418 device_hooks khangman_hooks={
419 	(device_open_hook)khangman_open,
420 	khangman_close,
421 	(device_free_hook)khangman_free,
422 	NULL,
423 	(device_read_hook)khangman_read,
424 	khangman_write,
425 	NULL,
426 	NULL,
427 	NULL,
428 	NULL
429 };
430 
431 
432 device_hooks *
433 find_device(const char *name)
434 {
435 	(void)name;
436 	return &khangman_hooks;
437 }
438 
439 
440 #  else /* as module */
441 
442 
443 status_t std_ops(int32 op, ...);
444 
445 status_t
446 std_ops(int32 op, ...)
447 {
448 	status_t err;
449 
450 	switch (op) {
451 	case B_MODULE_INIT:
452 		err = init_words(FORTUNE_FILE);
453 		if (err < B_OK) {
454 			dprintf("hangman: error reading fortune file: %s\n", strerror(err));
455 			return B_ERROR;
456 		}
457 		add_debugger_command("kdlhangman", kdlhangman, KCMD_HELP);
458 		return B_OK;
459 	case B_MODULE_UNINIT:
460 		remove_debugger_command("kdlhangman", kdlhangman);
461 		return B_ERROR;
462 	}
463 	return B_ERROR;
464 }
465 
466 
467 static module_info minfo = {
468 	"debugger/hangman/v1",
469 	B_KEEP_LOADED,
470 	&std_ops
471 };
472 
473 module_info *modules[] = { &minfo, NULL };
474 
475 #  endif /* AS_DRIVER */
476 
477 #else
478 
479 void
480 kdl_trip(void)
481 {
482 	int fd;
483 	fd = open("/dev/misc/hangman", O_WRONLY);
484 	if (fd < B_OK) {
485 		puts("hey, you're pissing me off, no /dev/"DEV_ENTRY" !!!");
486 		system("/bin/alert --stop 'It would work better with the hangman driver enabled...\nyou really deserves a forced reboot :P'");
487 		return;
488 	}
489 	write(fd, "hangme!", 7);
490 	close(fd);
491 }
492 
493 
494 int
495 main(int argc, char *argv)
496 {
497 	int score; /* how many correct guesses ? */
498 	/* init */
499 	if (init_words(FORTUNE_FILE) < B_OK) {
500 		fprintf(stderr, "error reading fortune file\n");
501 		return 1;
502 	}
503 	score = play_hangman();
504 	PRINTF("score %d\n", score);
505 	if (score > (MAX_CACHED_WORDS - MAX_FAILS_BEFORE_BSOD)) {
506 		PRINTF("Congrats !\n");
507 	}
508 	if (score < (MAX_CACHED_WORDS - MAX_FAILS_BEFORE_BSOD)) {
509 		/* too many fails... gonna kick :p */
510 		kdl_trip();
511 	}
512 	return 0;
513 }
514 
515 
516 #endif
517