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