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
46char 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 :) */
51static 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 */
62static 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
74char bigbuffer[BIGBUFFSZ];
75
76#define BIT_FROM_LETTER(l) (0x1 << (l - 'a'))
77
78status_t init_words(char *from);
79status_t init_words_from_threadnames(void);
80void print_hangman(int fails);
81void display_word(int current, uint32 tried_letters);
82int play_hangman(void);
83int kdlhangman(int argc, char **argv);
84
85#ifdef _KERNEL_MODE
86
87# ifdef __HAIKU__
88extern 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 */
96static char *(*bsod_gets)(char *, char *, int);
97extern char *(*bsod_kgets)(char *, char *, int);
98//extern char *bsod_gets(char *);
99/* saved here before panic()ing */
100char *(*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
116status_t
117init_words(char *from)
118{
119	int fd;
120	size_t sz, got, beg, end, i;
121	int current;
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
173status_t
174init_words_from_threadnames(void)
175{
176	size_t sz, got, beg, end, i;
177	int current;
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
218void
219print_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
231void
232display_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
243int
244play_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 (try == *str)
287								gotone = 1;
288							str++;
289						}
290					}
291				}
292				//PRINTF("gotone:%d, gotit:%d, tried_letters:%08lx\n", gotone, gotit, tried_letters);
293			} while(tried_letters != 0x03ffffff && !gotit && gotone);
294			if (gotit)
295				break;
296			print_hangman(bad_guesses+1);
297		}
298		if (bad_guesses < 6) {
299			display_word(current, 0x03ffffff);
300			if (strlen(words[current]) < 5)
301				PRINTF("That was easy :-P\n");
302			else if (strlen(words[current]) < 7)
303				PRINTF("Good one !\n");
304			else
305				PRINTF("You got this hard one ! :-)\n");
306			score ++;
307		}
308/**/
309		else return score;
310/**/
311	}
312	return score;
313}
314
315
316#ifdef _KERNEL_MODE /* driver parts */
317
318
319#ifndef __HAIKU__ /* BeOS intimacy revealed */
320//char *bsod_wrapper_gets(char *p, int len)
321//char *bsod_wrapper_gets(int len, char *p)
322char *
323bsod_wrapper_gets(char *prompt, char *p, int len)
324{
325	/* fall back to the normal gets() */
326	bsod_kgets = bsod_saved_kgets;
327//	if (!bsod_kgets)
328//		bsod_kgets = bsod_gets;
329	/* and fake some typing */
330	strcpy(p, fake_typed);
331	return p;
332}
333#else
334
335#endif
336
337
338int
339kdlhangman(int argc, char **argv)
340{
341	int score;
342
343	if (argc > 1 && strcmp(argv[1], "--help") == 0) {
344		PRINTF("%s\n", KCMD_HELP);
345		return 0;
346	}
347
348	score = play_hangman();
349PRINTF("score %d\n", score);
350	if (score > (MAX_CACHED_WORDS - MAX_FAILS_BEFORE_BSOD)) {
351		PRINTF("Congrats !\n");
352	}
353	if (score < (MAX_CACHED_WORDS - MAX_FAILS_BEFORE_BSOD)) {
354#ifdef FAIL_IN_BSOD_CAUSE_REBOOT
355		PRINTF("Hmmm, sorry, need to trash your hdd... Ok, just a reboot then\n");
356		fake_typed = "reboot";
357		bsod_kgets = bsod_wrapper_gets;
358		return 1;
359#else
360		PRINTF("Hmmm, sorry, need to trash your hdd... Well, I'll be nice this time\n");
361#endif
362	}
363	//return B_KDEBUG_CONT;
364	return B_KDEBUG_QUIT;
365}
366
367
368#  ifdef AS_DRIVER
369
370typedef struct {
371	int dummy;
372} cookie_t;
373
374const char * device_names[]={DEV_ENTRY, NULL};
375
376
377status_t
378init_hardware(void) {
379	return B_OK;
380}
381
382
383status_t
384init_driver(void)
385{
386	status_t err;
387
388	err = init_words(FORTUNE_FILE);
389	if (err < B_OK) {
390		dprintf("hangman: error reading fortune file: %s\n", strerror(err));
391		return B_ERROR;
392	}
393	get_image_symbol(KERNEL_IMAGE_ID, "bsod_gets", B_SYMBOL_TYPE_ANY, (void **)&bsod_gets);
394	add_debugger_command("kdlhangman", kdlhangman, KCMD_HELP);
395	return B_OK;
396}
397
398
399void
400uninit_driver(void)
401{
402	remove_debugger_command("kdlhangman", kdlhangman);
403}
404
405
406const char **
407publish_devices()
408{
409	return device_names;
410}
411
412
413status_t
414khangman_open(const char *name, uint32 flags, cookie_t **cookie)
415{
416	(void)name; (void)flags;
417	*cookie = (void*)malloc(sizeof(cookie_t));
418	if (*cookie == NULL) {
419		dprintf("khangman_open : error allocating cookie\n");
420		goto err0;
421	}
422	memset(*cookie, 0, sizeof(cookie_t));
423	return B_OK;
424err0:
425	return B_ERROR;
426}
427
428
429status_t
430khangman_close(void *cookie)
431{
432	(void)cookie;
433	return B_OK;
434}
435
436
437status_t
438khangman_free(cookie_t *cookie)
439{
440	free(cookie);
441	return B_OK;
442}
443
444
445status_t
446khangman_read(cookie_t *cookie, off_t position, void *data, size_t *numbytes)
447{
448	*numbytes = 0;
449	return B_NOT_ALLOWED;
450}
451
452
453status_t
454khangman_write(void *cookie, off_t position, const void *data, size_t *numbytes)
455{
456	(void)cookie; (void)position; (void)data; (void)numbytes;
457	//*numbytes = 0;
458	/* here we get to kdlhangman */
459	fake_typed = "kdlhangman";
460	bsod_saved_kgets = bsod_kgets;
461	bsod_kgets = bsod_wrapper_gets;
462	kernel_debugger("So much more fun in KDL...");
463
464	return B_OK;
465}
466
467
468device_hooks khangman_hooks={
469	(device_open_hook)khangman_open,
470	khangman_close,
471	(device_free_hook)khangman_free,
472	NULL,
473	(device_read_hook)khangman_read,
474	khangman_write,
475	NULL,
476	NULL,
477	NULL,
478	NULL
479};
480
481
482device_hooks *
483find_device(const char *name)
484{
485	(void)name;
486	return &khangman_hooks;
487}
488
489
490#  else /* as module */
491
492
493static status_t
494std_ops(int32 op, ...)
495{
496	status_t err;
497
498	switch (op) {
499		case B_MODULE_INIT:
500			err = init_words(FORTUNE_FILE);
501			if (err < B_OK) {
502				dprintf("hangman: error reading fortune file: %s\n",
503					strerror(err));
504				err = init_words_from_threadnames();
505				if (err < B_OK) {
506					dprintf("hangman: error getting thread names: %s\n",
507						strerror(err));
508					return B_ERROR;
509				}
510			}
511			add_debugger_command("kdlhangman", kdlhangman, KCMD_HELP);
512			return B_OK;
513		case B_MODULE_UNINIT:
514			remove_debugger_command("kdlhangman", kdlhangman);
515			return B_OK;
516	}
517
518	return B_ERROR;
519}
520
521
522static struct debugger_module_info sModuleInfo = {
523	{
524		"debugger/hangman/v1",
525		B_KEEP_LOADED,
526		&std_ops
527	},
528	NULL,
529	NULL,
530	NULL,
531	NULL
532};
533
534module_info *modules[] = {
535	(module_info *)&sModuleInfo,
536	NULL
537};
538
539#  endif /* AS_DRIVER */
540
541#else
542
543void
544kdl_trip(void)
545{
546	int fd;
547	fd = open("/dev/misc/hangman", O_WRONLY);
548	if (fd < B_OK) {
549		puts("hey, you're pissing me off, no /dev/"DEV_ENTRY" !!!");
550		system("/bin/alert --stop 'It would work better with the hangman driver enabled...\nyou really deserve a forced reboot :P'");
551		return;
552	}
553	write(fd, "hangme!", 7);
554	close(fd);
555}
556
557
558int
559main(int argc, char *argv)
560{
561	int score; /* how many correct guesses ? */
562	/* init */
563	if (init_words(FORTUNE_FILE) < B_OK) {
564		fprintf(stderr, "error reading fortune file\n");
565		return 1;
566	}
567	score = play_hangman();
568	PRINTF("score %d\n", score);
569	if (score > (MAX_CACHED_WORDS - MAX_FAILS_BEFORE_BSOD)) {
570		PRINTF("Congrats !\n");
571	}
572	if (score < (MAX_CACHED_WORDS - MAX_FAILS_BEFORE_BSOD)) {
573		/* too many fails... gonna kick :p */
574		kdl_trip();
575	}
576	return 0;
577}
578
579
580#endif
581