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