cgram.c revision 1.4 1 /* $NetBSD */
2
3 /*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by David A. Holland.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <time.h>
37 #include <err.h>
38 #include <assert.h>
39 #include <curses.h>
40 #include "pathnames.h"
41
42 ////////////////////////////////////////////////////////////
43
44 static char *
45 xstrdup(const char *s)
46 {
47 char *ret;
48
49 ret = malloc(strlen(s) + 1);
50 if (ret == NULL) {
51 errx(1, "Out of memory");
52 }
53 strcpy(ret, s);
54 return ret;
55 }
56
57 ////////////////////////////////////////////////////////////
58
59 struct stringarray {
60 char **v;
61 int num;
62 };
63
64 static void
65 stringarray_init(struct stringarray *a)
66 {
67 a->v = NULL;
68 a->num = 0;
69 }
70
71 static void
72 stringarray_cleanup(struct stringarray *a)
73 {
74 free(a->v);
75 }
76
77 static void
78 stringarray_add(struct stringarray *a, const char *s)
79 {
80 a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0]));
81 if (a->v == NULL) {
82 errx(1, "Out of memory");
83 }
84 a->v[a->num] = xstrdup(s);
85 a->num++;
86 }
87
88 ////////////////////////////////////////////////////////////
89
90 static struct stringarray lines;
91 static struct stringarray sollines;
92 static bool hinting;
93 static int scrolldown;
94 static unsigned curx;
95 static int cury;
96
97 static void
98 readquote(void)
99 {
100 FILE *f = popen(_PATH_FORTUNE, "r");
101 if (!f) {
102 err(1, "%s", _PATH_FORTUNE);
103 }
104
105 char buf[128], buf2[8 * sizeof(buf)];
106 while (fgets(buf, sizeof(buf), f)) {
107 char *s = strrchr(buf, '\n');
108 assert(s);
109 assert(strlen(s) == 1);
110 *s = 0;
111
112 int i, j;
113 for (i = j = 0; buf[i]; i++) {
114 if (buf[i] == '\t') {
115 buf2[j++] = ' ';
116 while (j % 8)
117 buf2[j++] = ' ';
118 } else if (buf[i] == '\b') {
119 if (j > 0)
120 j--;
121 } else {
122 buf2[j++] = buf[i];
123 }
124 }
125 buf2[j] = 0;
126
127 stringarray_add(&lines, buf2);
128 stringarray_add(&sollines, buf2);
129 }
130
131 pclose(f);
132 }
133
134 static void
135 encode(void)
136 {
137 int key[26];
138 for (int i = 0; i < 26; i++)
139 key[i] = i;
140 for (int i = 26; i > 1; i--) {
141 int c = random() % i;
142 int t = key[i - 1];
143 key[i - 1] = key[c];
144 key[c] = t;
145 }
146
147 for (int y = 0; y < lines.num; y++) {
148 for (unsigned x = 0; lines.v[y][x]; x++) {
149 if (islower((unsigned char)lines.v[y][x])) {
150 int q = lines.v[y][x] - 'a';
151 lines.v[y][x] = 'a' + key[q];
152 }
153 if (isupper((unsigned char)lines.v[y][x])) {
154 int q = lines.v[y][x] - 'A';
155 lines.v[y][x] = 'A' + key[q];
156 }
157 }
158 }
159 }
160
161 static int
162 substitute(int ch)
163 {
164 assert(cury >= 0 && cury < lines.num);
165 if (curx >= strlen(lines.v[cury])) {
166 beep();
167 return -1;
168 }
169
170 int och = lines.v[cury][curx];
171 if (!isalpha((unsigned char)och)) {
172 beep();
173 return -1;
174 }
175
176 int loch = tolower((unsigned char)och);
177 int uoch = toupper((unsigned char)och);
178 int lch = tolower((unsigned char)ch);
179 int uch = toupper((unsigned char)ch);
180
181 for (int y = 0; y < lines.num; y++) {
182 for (unsigned x = 0; lines.v[y][x]; x++) {
183 if (lines.v[y][x] == loch) {
184 lines.v[y][x] = lch;
185 } else if (lines.v[y][x] == uoch) {
186 lines.v[y][x] = uch;
187 } else if (lines.v[y][x] == lch) {
188 lines.v[y][x] = loch;
189 } else if (lines.v[y][x] == uch) {
190 lines.v[y][x] = uoch;
191 }
192 }
193 }
194 return 0;
195 }
196
197 ////////////////////////////////////////////////////////////
198
199 static void
200 redraw(void)
201 {
202 erase();
203 bool won = true;
204 for (int i = 0; i < LINES - 1; i++) {
205 move(i, 0);
206 int ln = i + scrolldown;
207 if (ln < lines.num) {
208 for (unsigned j = 0; lines.v[i][j]; j++) {
209 int ch = lines.v[i][j];
210 if (ch != sollines.v[i][j] &&
211 isalpha((unsigned char)ch)) {
212 won = false;
213 }
214 bool bold = false;
215 if (hinting && ch == sollines.v[i][j] &&
216 isalpha((unsigned char)ch)) {
217 bold = true;
218 attron(A_BOLD);
219 }
220 addch(lines.v[i][j]);
221 if (bold) {
222 attroff(A_BOLD);
223 }
224 }
225 }
226 clrtoeol();
227 }
228
229 move(LINES - 1, 0);
230 if (won) {
231 addstr("*solved* ");
232 }
233 addstr("~ to quit, * to cheat, ^pnfb to move");
234
235 move(LINES - 1, 0);
236
237 move(cury - scrolldown, curx);
238
239 refresh();
240 }
241
242 static void
243 opencurses(void)
244 {
245 initscr();
246 cbreak();
247 noecho();
248 }
249
250 static void
251 closecurses(void)
252 {
253 endwin();
254 }
255
256 ////////////////////////////////////////////////////////////
257
258 static void
259 loop(void)
260 {
261 bool done = false;
262 while (!done) {
263 redraw();
264 int ch = getch();
265 switch (ch) {
266 case 1: /* ^A */
267 case KEY_HOME:
268 curx = 0;
269 break;
270 case 2: /* ^B */
271 case KEY_LEFT:
272 if (curx > 0) {
273 curx--;
274 } else if (cury > 0) {
275 cury--;
276 curx = strlen(lines.v[cury]);
277 }
278 break;
279 case 5: /* ^E */
280 case KEY_END:
281 curx = strlen(lines.v[cury]);
282 break;
283 case 6: /* ^F */
284 case KEY_RIGHT:
285 if (curx < strlen(lines.v[cury])) {
286 curx++;
287 } else if (cury < lines.num - 1) {
288 cury++;
289 curx = 0;
290 }
291 break;
292 case 12: /* ^L */
293 clear();
294 break;
295 case 14: /* ^N */
296 case KEY_DOWN:
297 if (cury < lines.num - 1) {
298 cury++;
299 }
300 if (curx > strlen(lines.v[cury])) {
301 curx = strlen(lines.v[cury]);
302 }
303 if (scrolldown < cury - (LINES - 2)) {
304 scrolldown = cury - (LINES - 2);
305 }
306 break;
307 case 16: /* ^P */
308 case KEY_UP:
309 if (cury > 0) {
310 cury--;
311 }
312 if (curx > strlen(lines.v[cury])) {
313 curx = strlen(lines.v[cury]);
314 }
315 if (scrolldown > cury) {
316 scrolldown = cury;
317 }
318 break;
319 case '*':
320 hinting = !hinting;
321 break;
322 case '~':
323 done = true;
324 break;
325 default:
326 if (isalpha(ch)) {
327 if (!substitute(ch)) {
328 if (curx < strlen(lines.v[cury])) {
329 curx++;
330 }
331 if (curx == strlen(lines.v[cury]) &&
332 cury < lines.num - 1) {
333 curx = 0;
334 cury++;
335 }
336 }
337 } else if (curx < strlen(lines.v[cury]) &&
338 ch == lines.v[cury][curx]) {
339 curx++;
340 if (curx == strlen(lines.v[cury]) &&
341 cury < lines.num - 1) {
342 curx = 0;
343 cury++;
344 }
345 } else {
346 beep();
347 }
348 break;
349 }
350 }
351 }
352
353 ////////////////////////////////////////////////////////////
354
355 int
356 main(void)
357 {
358
359 stringarray_init(&lines);
360 stringarray_init(&sollines);
361 srandom(time(NULL));
362 readquote();
363 encode();
364 opencurses();
365
366 keypad(stdscr, TRUE);
367 loop();
368
369 closecurses();
370 stringarray_cleanup(&sollines);
371 stringarray_cleanup(&lines);
372 }
373