/*- * Copyright (c) 2013 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by David A. Holland. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "pathnames.h" //////////////////////////////////////////////////////////// static char * xstrdup(const char *s) { char *ret; ret = malloc(strlen(s) + 1); if (ret == NULL) errx(1, "Out of memory"); strcpy(ret, s); return ret; } //////////////////////////////////////////////////////////// struct stringarray { char **v; int num; }; static void stringarray_init(struct stringarray *a) { a->v = NULL; a->num = 0; } static void stringarray_cleanup(struct stringarray *a) { free(a->v); } static void stringarray_add(struct stringarray *a, const char *s) { a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0])); if (a->v == NULL) errx(1, "Out of memory"); a->v[a->num] = xstrdup(s); a->num++; } //////////////////////////////////////////////////////////// static struct stringarray lines; static struct stringarray sollines; static bool hinting; static int scrolldown; static unsigned curx; static int cury; static void readquote(void) { FILE *f = popen(_PATH_FORTUNE, "r"); if (!f) err(1, "%s", _PATH_FORTUNE); char buf[128], buf2[8 * sizeof(buf)]; while (fgets(buf, sizeof(buf), f)) { char *s = strrchr(buf, '\n'); assert(s && strlen(s) == 1); *s = '\0'; int i, j; for (i = j = 0; buf[i]; i++) { if (buf[i] == '\t') { buf2[j++] = ' '; while (j % 8) buf2[j++] = ' '; } else if (buf[i] == '\b') { if (j > 0) j--; } else { buf2[j++] = buf[i]; } } buf2[j] = 0; stringarray_add(&lines, buf2); stringarray_add(&sollines, buf2); } pclose(f); } static void encode(void) { int key[26]; for (int i = 0; i < 26; i++) key[i] = i; for (int i = 26; i > 1; i--) { int c = random() % i; int t = key[i - 1]; key[i - 1] = key[c]; key[c] = t; } for (int y = 0; y < lines.num; y++) { for (int x = 0; lines.v[y][x]; x++) { if (islower((unsigned char)lines.v[y][x])) { int q = lines.v[y][x] - 'a'; lines.v[y][x] = 'a' + key[q]; } if (isupper((unsigned char)lines.v[y][x])) { int q = lines.v[y][x] - 'A'; lines.v[y][x] = 'A' + key[q]; } } } } static int substitute(int ch) { assert(cury >= 0 && cury < lines.num); if (curx >= strlen(lines.v[cury])) { beep(); return -1; } int och = (unsigned char)lines.v[cury][curx]; if (!isalpha(och)) { beep(); return -1; } int loch = tolower(och); int uoch = toupper(och); int lch = tolower(ch); int uch = toupper(ch); for (int y = 0; y < lines.num; y++) { for (int x = 0; lines.v[y][x]; x++) { if (lines.v[y][x] == loch) lines.v[y][x] = lch; else if (lines.v[y][x] == uoch) lines.v[y][x] = uch; else if (lines.v[y][x] == lch) lines.v[y][x] = loch; else if (lines.v[y][x] == uch) lines.v[y][x] = uoch; } } return 0; } //////////////////////////////////////////////////////////// static void redraw(void) { erase(); bool won = true; for (int i = 0; i < LINES - 1; i++) { move(i, 0); int ln = i + scrolldown; if (ln < lines.num) { for (int j = 0; lines.v[i][j]; j++) { int ch = (unsigned char)lines.v[i][j]; int solch = (unsigned char)sollines.v[i][j]; if (ch != solch && isalpha(ch)) won = false; int attr = 0; if (hinting && ch == solch && isalpha(ch)) attr = A_BOLD; addch(ch | attr); } } clrtoeol(); } move(LINES - 1, 0); if (won) addstr("*solved* "); addstr("~ to quit, * to cheat, ^pnfb to move"); move(cury - scrolldown, curx); refresh(); } static void opencurses(void) { initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); } static void closecurses(void) { endwin(); } //////////////////////////////////////////////////////////// static void loop(void) { bool done = false; while (!done) { redraw(); int ch = getch(); switch (ch) { case 1: /* ^A */ case KEY_HOME: curx = 0; break; case 2: /* ^B */ case KEY_LEFT: if (curx > 0) { curx--; } else if (cury > 0) { cury--; curx = strlen(lines.v[cury]); } break; case 5: /* ^E */ case KEY_END: curx = strlen(lines.v[cury]); break; case 6: /* ^F */ case KEY_RIGHT: if (curx < strlen(lines.v[cury])) { curx++; } else if (cury < lines.num - 1) { cury++; curx = 0; } break; case 12: /* ^L */ clear(); break; case 14: /* ^N */ case KEY_DOWN: if (cury < lines.num - 1) cury++; if (curx > strlen(lines.v[cury])) curx = strlen(lines.v[cury]); if (scrolldown < cury - (LINES - 2)) scrolldown = cury - (LINES - 2); break; case 16: /* ^P */ case KEY_UP: if (cury > 0) cury--; if (curx > strlen(lines.v[cury])) curx = strlen(lines.v[cury]); if (scrolldown > cury) scrolldown = cury; break; case '*': hinting = !hinting; break; case '~': done = true; break; default: if (isalpha(ch)) { if (!substitute(ch)) { if (curx < strlen(lines.v[cury])) curx++; if (curx == strlen(lines.v[cury]) && cury < lines.num - 1) { curx = 0; cury++; } } } else if (curx < strlen(lines.v[cury]) && ch == lines.v[cury][curx]) { curx++; if (curx == strlen(lines.v[cury]) && cury < lines.num - 1) { curx = 0; cury++; } } else { beep(); } break; } } } //////////////////////////////////////////////////////////// int main(void) { stringarray_init(&lines); stringarray_init(&sollines); srandom(time(NULL)); readquote(); encode(); opencurses(); loop(); closecurses(); stringarray_cleanup(&sollines); stringarray_cleanup(&lines); return 0; }