/** \brief Main function. * * @param argc Number of arguments from the system. * @param argv Arguments from the system. * @return Program exit code. */ int main(int argc, char **argv) { // Option arguments. static const struct option opts_long[] = { { "2chan", no_argument, NULL, '2' }, { "benchmark", no_argument, NULL, 'h' }, { "enable-case", no_argument, NULL, 'c' }, { "help", no_argument, NULL, 'h' }, { "enable-leet", no_argument, NULL, 'l' }, { "generate", no_argument, NULL, 'g' }, { "nthreads", required_argument, NULL, 'n' }, { "progress-file", required_argument, NULL, 'p' }, { "start-from", required_argument, NULL, 's' }, { "wildcard", no_argument, NULL, 'w' }, { NULL, 0, 0, 0 } }; static const char *opts_short = "2cbhlgn:p:s:w"; // Local args. uint8_t enable_generate = 0, enable_leet = 0, enable_case = 0, enable_wildcard = 0; while(1) { int indexptr = 0; int opt = getopt_long(argc, argv, opts_short, opts_long, &indexptr); if(opt == -1) { break; } switch(opt) { case '2': if(einfo.name) { fputs("Tripcode algorithm may only be specified once.", stderr); exit_cleanup(); return 1; } einfo = encrypt_info_2chan; break; case 'b': flag_print_benchmarks = 1; break; case 'c': enable_case = 1; break; case 'h': puts(usage); exit_cleanup(); return 0; case 'l': enable_leet = 1; break; case 'g': enable_generate = 1; break; case 'n': thread_count = strtol(optarg, NULL, 10); if((thread_count == LONG_MAX) || (thread_count == LONG_MIN)) { fprintf(stderr, "Invalid thread count: %i\n", (int)thread_count); exit_cleanup(); return 1; } break; case 'p': if(progress_filename) { fputs("Progress file may only be specified once.", stderr); exit_cleanup(); return 1; } progress_filename = strdup(optarg); break; case 's': if(current_tripcode) { fputs("Starting code may only be specified once.", stderr); exit_cleanup(); return 1; } current_tripcode_len = strlen(optarg); current_tripcode = memdup(optarg, current_tripcode_len + 1); printf("Using starting code: %s\n", current_tripcode); break; case 'w': enable_wildcard = 1; break; default: puts(usage); exit_cleanup(); return 1; } } while(optind < argc) { char *opt = argv[optind++]; size_t len = strlen(opt); if(len > 0) { if(!search_tripcodes) { search_tripcodes = (tripcode_t*)malloc(sizeof(tripcode_t)); search_tripcode_count = 1; } else { search_tripcodes = (tripcode_t*)realloc(search_tripcodes, sizeof(tripcode_t) * (++search_tripcode_count)); } tripcode_t *trip = search_tripcodes + (search_tripcode_count - 1); trip->trip = (char*)memdup(opt, len + 1); trip->len = len; } else { fputs("Empty string are not valid searching.", stderr); return 1; } } // Sanity check for tripcode count. if(search_tripcode_count <= 0) { fprintf(stderr, "Please specify at least one tripcode.\n"); return 1; } // If no algo selected yet, pick 2chan. if(!einfo.name) { einfo = encrypt_info_2chan; } printf("Using algorithm: %s\n", einfo.name); str_enumerate_init(einfo.search_space); // Thread cap based on search space size. { long int sspacesize = get_search_space_size(); if(sspacesize < thread_count) { printf("WARNING: current search space limits to %i threads\n", (int)sspacesize); thread_count = sspacesize; } } // Decide character transform. if(!enable_generate) { char_transform_func = char_transform_identity; strstr_func = strstr_normal; printf("Using character transform: "); if(enable_case && !enable_leet && !enable_wildcard) { puts("none"); } else { unsigned flag_line = 0; if(enable_case) { if(enable_leet) { flag_line = fprint_list_spacing(stdout, flag_line); printf("1337"); char_transform_func = char_transform_leet; } } else { flag_line = fprint_list_spacing(stdout, flag_line); printf("case_insensitive"); char_transform_func = char_transform_nocase; if(enable_leet) { flag_line = fprint_list_spacing(stdout, flag_line); printf("1337"); char_transform_func = char_transform_nocase_leet; } } if(enable_wildcard) { flag_line = fprint_list_spacing(stdout, flag_line); printf("wildcard"); strstr_func = strstr_wildcard; } puts(""); } } // If generate trip requested, do it and exit. if(enable_generate) { for(size_t ii = 0; (ii < search_tripcode_count); ++ii) { tripcode_t *trip = search_tripcodes + ii; char *enc = einfo.encrypt_function(trip->trip, trip->len); printf("Password %s encrypts to tripcode %s\n", trip->trip, enc); free(enc); } exit_cleanup(); return 0; } // Sanity check for tripcode lengths. for(size_t ii = 0; (ii < search_tripcode_count); ++ii) { tripcode_t *trip = search_tripcodes + ii; if(trip->len > einfo.max_code_length) { fprintf(stderr, "Code %s is %u chars long, too much for current algo (%u).\n", trip->trip, (unsigned)(trip->len), (unsigned)(einfo.max_code_length - 1)); exit_cleanup(); return 1; } // Perform case transform in precalc! for(size_t jj = 0; (jj < trip->len); ++jj) { trip->trip[jj] = (char)char_transform_func(trip->trip[jj]); } } // Only read current tripcode if it's not yet specified. if(progress_filename) { char *prog = progress_read(progress_filename); if(prog) { if(current_tripcode) { fprintf(stderr, "Not overwriting starting code from file: %s\n", prog); free(prog); } else { printf("Using starting code from file: %s\n", prog); current_tripcode = prog; current_tripcode_len = strlen(prog); } } } // Try the initial tripcode if it has been specified. int64_t benchmark_processed = 0; if(current_tripcode) { benchmark_processed += einfo.test_function(current_tripcode, current_tripcode_len, stdout); } pthread_cond_init(&term_cond, NULL); pthread_mutex_init(&term_mutex, NULL); signal(SIGINT, tripcrunch_signal_handler); signal(SIGTERM, tripcrunch_signal_handler); // Enter critical section and create all threads. pthread_mutex_lock(&term_mutex); pthread_t *threads = (pthread_t*)malloc(sizeof(pthread_t) * (unsigned)thread_count); for(int ii = 0; (ii < thread_count); ++ii) { // Reserve the thread info for passing to the threads. thread_info_t *tinfo = (thread_info_t*)malloc(sizeof(thread_info_t)); // Give the next tripcode to the string. current_tripcode = str_enumerate_1(current_tripcode, ¤t_tripcode_len); tinfo->trip.trip = memdup(current_tripcode, current_tripcode_len + 1); tinfo->trip.len = current_tripcode_len; int err = pthread_create(threads + ii, NULL, threadfunc_tripcrunch, tinfo); if(err) { fprintf(stderr, "ERROR %s\n", strerror(err)); return 1; // Should never happen, okay to not clean up. } } // Wait for exit, then leave critical section. int64_t benchmark_start = get_current_time_int64(); pthread_mutex_unlock(&term_mutex); // Immediately start joining the threads. for(int ii = 0; (ii < thread_count); ++ii) { void **thr_ret = NULL; pthread_join(threads[ii], thr_ret); if(!thr_ret) { fprintf(stderr, "ERROR: no return value from thread %i\n", ii); return 1; // Should never happen, okay to not clean up. } thread_info_t *tinfo = (thread_info_t*)(*thr_ret); char *trip = tinfo->trip.trip; size_t len = tinfo->trip.len; int64_t count = tinfo->count; printf("Thread %i: %s (%.0f trips)\n", ii, tinfo->trip.trip, (double)(tinfo->count)); benchmark_processed += count; int cmp = str_enumcmp(current_tripcode, current_tripcode_len, trip, len); if((cmp > 0) || ((ii <= 0) && count)) { free(current_tripcode); current_tripcode = memdup(trip, len + 1); current_tripcode_len = len; } free(trip); free(tinfo); } // All threads have been joined, time to end the benchmark and free the // thread table. int64_t benchmark_end = get_current_time_int64(); free(threads); // Must save progress before other cleanup. if(progress_filename) { progress_save(progress_filename, current_tripcode); } // Current tripcode is not necessarily initialized. if(current_tripcode) { printf("Last search: %s\n", current_tripcode); } exit_cleanup(); // Only print benchmarks if requested. if(flag_print_benchmarks) { double trips = (double)benchmark_processed, secs = (double)(benchmark_end - benchmark_start) / 1000000.0; printf("Benchmark: %.0f trips / %.2f secs -> %.2f trips/sec\n", trips, secs, trips / secs); } pthread_cond_destroy(&term_cond); pthread_mutex_destroy(&term_mutex); return 0; }
int main(int argc, char **argv) { char user[64]; screen_init(); if (setjmp(fatal_error_jmp_buf) == 0) { char *setname = DEFAULTSET; levelset *set; level *l; gamestate *gs; progress p; int i, action, n, saveslot = 0; gamestate *saves[10]; get_user(user, sizeof(user)); /* * Pick up argv[1] in case it describes an alternate level * set. */ if (argc > 1) setname = argv[1]; set = levelset_load(setname); p = progress_load(set, user); for (i = 0; i < 10; i++) saves[i] = savepos_load(set, user, i); while (1) { action = screen_main_menu(set, saves, p.levnum+1, p.levnum); if (action > 0) { l = set->levels[action-1]; gs = init_game(l); gs->levnum = action; saveslot = 0; } else if (action == -100) { break; /* direct quit from main menu */ } else if (action <= -10) { /* delete a saved position */ n = -action-10; if (saves[n]) gamestate_free(saves[n]); saves[n] = NULL; savepos_del(set, user, n); gs = NULL; } else { /* load a saved position */ n = -action; if (!saves[n]) /* don't segfault */ continue; gs = gamestate_copy(saves[n]); l = set->levels[gs->levnum-1]; saveslot = n; } if (gs) { screen_level_init(); while (gs->status == PLAYING) { gamestate *newgs; int k; screen_level_display(gs, NULL); k = screen_level_getmove(); if (k == 'h' || k == 'j' || k == 'l' || k == 'k') { newgs = make_move(gs, k); gamestate_free(gs); gs = newgs; } else if (k == 's') { n = screen_saveslot_ask('s', saves, saveslot); if (n >= 0) { saveslot = n; saves[saveslot] = gamestate_copy(gs); savepos_save(set, user, saveslot, gs); } } else if (k == 'r') { n = screen_saveslot_ask('r', saves, saveslot); if (n >= 0 && saves[n]) { saveslot = n; gamestate_free(gs); gs = gamestate_copy(saves[saveslot]); l = set->levels[gs->levnum-1]; } } else if (k == 'q') { break; } } if (gs->status != PLAYING) { int increased_level = FALSE; char *msg; if (gs->status == DIED) { msg = "GAME OVER"; } else if (gs->status == COMPLETED) { msg = "LEVEL COMPLETE"; if (p.levnum < gs->levnum) { p.levnum = gs->levnum; p.date = time(NULL); progress_save(set, user, p); increased_level = TRUE; } } else { msg = "!INTERNAL ERROR!"; } screen_level_display(gs, msg); screen_level_finish(); if (increased_level && p.levnum == set->nlevels) { screen_completed_game(); } } } } } else { screen_finish(); fprintf(stderr, "Fatal error: %s\n", fatal_error_string); exit(2); } screen_finish(); return 0; }