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