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