cgram.c revision 1.9 1 1.9 rillig /* $NetBSD: cgram.c,v 1.9 2021/02/21 17:16:00 rillig Exp $ */
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.6 rillig #include <stdbool.h>
37 1.1 dholland #include <stdio.h>
38 1.5 rillig #include <stdlib.h>
39 1.1 dholland #include <string.h>
40 1.1 dholland #include <time.h>
41 1.5 rillig
42 1.1 dholland #include "pathnames.h"
43 1.1 dholland
44 1.1 dholland ////////////////////////////////////////////////////////////
45 1.1 dholland
46 1.4 rillig static char *
47 1.4 rillig xstrdup(const char *s)
48 1.4 rillig {
49 1.4 rillig char *ret;
50 1.4 rillig
51 1.4 rillig ret = malloc(strlen(s) + 1);
52 1.4 rillig if (ret == NULL) {
53 1.4 rillig errx(1, "Out of memory");
54 1.4 rillig }
55 1.4 rillig strcpy(ret, s);
56 1.4 rillig return ret;
57 1.1 dholland }
58 1.1 dholland
59 1.8 rillig static char
60 1.8 rillig ch_toupper(char ch)
61 1.8 rillig {
62 1.8 rillig return (char)toupper((unsigned char)ch);
63 1.8 rillig }
64 1.8 rillig
65 1.8 rillig static char
66 1.8 rillig ch_tolower(char ch)
67 1.8 rillig {
68 1.8 rillig return (char)tolower((unsigned char)ch);
69 1.8 rillig }
70 1.8 rillig
71 1.8 rillig static bool
72 1.8 rillig ch_isalpha(char ch)
73 1.8 rillig {
74 1.8 rillig return isalpha((unsigned char)ch);
75 1.8 rillig }
76 1.8 rillig
77 1.8 rillig static bool
78 1.8 rillig ch_islower(char ch)
79 1.8 rillig {
80 1.8 rillig return islower((unsigned char)ch);
81 1.8 rillig }
82 1.8 rillig
83 1.8 rillig static bool
84 1.8 rillig ch_isupper(char ch)
85 1.8 rillig {
86 1.8 rillig return isupper((unsigned char)ch);
87 1.8 rillig }
88 1.8 rillig
89 1.1 dholland ////////////////////////////////////////////////////////////
90 1.1 dholland
91 1.1 dholland struct stringarray {
92 1.4 rillig char **v;
93 1.9 rillig size_t num;
94 1.1 dholland };
95 1.1 dholland
96 1.4 rillig static void
97 1.4 rillig stringarray_init(struct stringarray *a)
98 1.4 rillig {
99 1.4 rillig a->v = NULL;
100 1.4 rillig a->num = 0;
101 1.4 rillig }
102 1.4 rillig
103 1.4 rillig static void
104 1.4 rillig stringarray_cleanup(struct stringarray *a)
105 1.4 rillig {
106 1.4 rillig free(a->v);
107 1.4 rillig }
108 1.4 rillig
109 1.4 rillig static void
110 1.4 rillig stringarray_add(struct stringarray *a, const char *s)
111 1.4 rillig {
112 1.4 rillig a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0]));
113 1.4 rillig if (a->v == NULL) {
114 1.4 rillig errx(1, "Out of memory");
115 1.4 rillig }
116 1.4 rillig a->v[a->num] = xstrdup(s);
117 1.4 rillig a->num++;
118 1.1 dholland }
119 1.1 dholland
120 1.1 dholland ////////////////////////////////////////////////////////////
121 1.1 dholland
122 1.1 dholland static struct stringarray lines;
123 1.1 dholland static struct stringarray sollines;
124 1.1 dholland static bool hinting;
125 1.1 dholland static int scrolldown;
126 1.9 rillig static int curx;
127 1.1 dholland static int cury;
128 1.1 dholland
129 1.4 rillig static void
130 1.4 rillig readquote(void)
131 1.4 rillig {
132 1.4 rillig FILE *f = popen(_PATH_FORTUNE, "r");
133 1.6 rillig if (f == NULL) {
134 1.4 rillig err(1, "%s", _PATH_FORTUNE);
135 1.4 rillig }
136 1.4 rillig
137 1.4 rillig char buf[128], buf2[8 * sizeof(buf)];
138 1.6 rillig while (fgets(buf, sizeof buf, f) != NULL) {
139 1.4 rillig char *s = strrchr(buf, '\n');
140 1.6 rillig assert(s != NULL);
141 1.4 rillig assert(strlen(s) == 1);
142 1.6 rillig *s = '\0';
143 1.4 rillig
144 1.4 rillig int i, j;
145 1.6 rillig for (i = j = 0; buf[i] != '\0'; i++) {
146 1.4 rillig if (buf[i] == '\t') {
147 1.4 rillig buf2[j++] = ' ';
148 1.6 rillig while (j % 8 != 0)
149 1.4 rillig buf2[j++] = ' ';
150 1.4 rillig } else if (buf[i] == '\b') {
151 1.4 rillig if (j > 0)
152 1.4 rillig j--;
153 1.4 rillig } else {
154 1.4 rillig buf2[j++] = buf[i];
155 1.4 rillig }
156 1.4 rillig }
157 1.6 rillig buf2[j] = '\0';
158 1.4 rillig
159 1.4 rillig stringarray_add(&lines, buf2);
160 1.4 rillig stringarray_add(&sollines, buf2);
161 1.4 rillig }
162 1.4 rillig
163 1.4 rillig pclose(f);
164 1.4 rillig }
165 1.4 rillig
166 1.4 rillig static void
167 1.4 rillig encode(void)
168 1.4 rillig {
169 1.4 rillig int key[26];
170 1.9 rillig
171 1.4 rillig for (int i = 0; i < 26; i++)
172 1.4 rillig key[i] = i;
173 1.9 rillig
174 1.4 rillig for (int i = 26; i > 1; i--) {
175 1.9 rillig int c = (int)(random() % i);
176 1.4 rillig int t = key[i - 1];
177 1.4 rillig key[i - 1] = key[c];
178 1.4 rillig key[c] = t;
179 1.4 rillig }
180 1.4 rillig
181 1.9 rillig for (int y = 0; y < (int)lines.num; y++) {
182 1.9 rillig for (char *p = lines.v[y]; *p != '\0'; p++) {
183 1.9 rillig if (ch_islower(*p))
184 1.9 rillig *p = (char)('a' + key[*p - 'a']);
185 1.9 rillig if (ch_isupper(*p))
186 1.9 rillig *p = (char)('A' + key[*p - 'A']);
187 1.4 rillig }
188 1.4 rillig }
189 1.4 rillig }
190 1.4 rillig
191 1.6 rillig static bool
192 1.9 rillig substitute(char ch)
193 1.4 rillig {
194 1.9 rillig assert(cury >= 0 && cury < (int)lines.num);
195 1.9 rillig if (curx >= (int)strlen(lines.v[cury])) {
196 1.4 rillig beep();
197 1.6 rillig return false;
198 1.4 rillig }
199 1.4 rillig
200 1.8 rillig char och = lines.v[cury][curx];
201 1.8 rillig if (!ch_isalpha(och)) {
202 1.4 rillig beep();
203 1.6 rillig return false;
204 1.4 rillig }
205 1.4 rillig
206 1.8 rillig char loch = ch_tolower(och);
207 1.8 rillig char uoch = ch_toupper(och);
208 1.8 rillig char lch = ch_tolower(ch);
209 1.8 rillig char uch = ch_toupper(ch);
210 1.4 rillig
211 1.9 rillig for (int y = 0; y < (int)lines.num; y++) {
212 1.9 rillig for (char *p = lines.v[y]; *p != '\0'; p++) {
213 1.9 rillig if (*p == loch)
214 1.9 rillig *p = lch;
215 1.9 rillig else if (*p == uoch)
216 1.9 rillig *p = uch;
217 1.9 rillig else if (*p == lch)
218 1.9 rillig *p = loch;
219 1.9 rillig else if (*p == uch)
220 1.9 rillig *p = uoch;
221 1.4 rillig }
222 1.4 rillig }
223 1.6 rillig return true;
224 1.1 dholland }
225 1.1 dholland
226 1.1 dholland ////////////////////////////////////////////////////////////
227 1.1 dholland
228 1.4 rillig static void
229 1.4 rillig redraw(void)
230 1.4 rillig {
231 1.4 rillig erase();
232 1.4 rillig bool won = true;
233 1.4 rillig for (int i = 0; i < LINES - 1; i++) {
234 1.4 rillig move(i, 0);
235 1.4 rillig int ln = i + scrolldown;
236 1.9 rillig if (ln < (int)lines.num) {
237 1.6 rillig for (unsigned j = 0; lines.v[i][j] != '\0'; j++) {
238 1.8 rillig char ch = lines.v[i][j];
239 1.8 rillig if (ch != sollines.v[i][j] && ch_isalpha(ch)) {
240 1.4 rillig won = false;
241 1.4 rillig }
242 1.4 rillig bool bold = false;
243 1.4 rillig if (hinting && ch == sollines.v[i][j] &&
244 1.8 rillig ch_isalpha(ch)) {
245 1.4 rillig bold = true;
246 1.4 rillig attron(A_BOLD);
247 1.4 rillig }
248 1.4 rillig addch(lines.v[i][j]);
249 1.4 rillig if (bold) {
250 1.4 rillig attroff(A_BOLD);
251 1.4 rillig }
252 1.4 rillig }
253 1.4 rillig }
254 1.4 rillig clrtoeol();
255 1.4 rillig }
256 1.4 rillig
257 1.4 rillig move(LINES - 1, 0);
258 1.4 rillig if (won) {
259 1.4 rillig addstr("*solved* ");
260 1.4 rillig }
261 1.4 rillig addstr("~ to quit, * to cheat, ^pnfb to move");
262 1.4 rillig
263 1.4 rillig move(LINES - 1, 0);
264 1.4 rillig
265 1.4 rillig move(cury - scrolldown, curx);
266 1.4 rillig
267 1.4 rillig refresh();
268 1.4 rillig }
269 1.4 rillig
270 1.4 rillig static void
271 1.4 rillig opencurses(void)
272 1.4 rillig {
273 1.4 rillig initscr();
274 1.4 rillig cbreak();
275 1.4 rillig noecho();
276 1.4 rillig }
277 1.4 rillig
278 1.4 rillig static void
279 1.4 rillig closecurses(void)
280 1.4 rillig {
281 1.4 rillig endwin();
282 1.1 dholland }
283 1.1 dholland
284 1.1 dholland ////////////////////////////////////////////////////////////
285 1.1 dholland
286 1.4 rillig static void
287 1.4 rillig loop(void)
288 1.4 rillig {
289 1.4 rillig bool done = false;
290 1.4 rillig while (!done) {
291 1.4 rillig redraw();
292 1.4 rillig int ch = getch();
293 1.4 rillig switch (ch) {
294 1.4 rillig case 1: /* ^A */
295 1.4 rillig case KEY_HOME:
296 1.4 rillig curx = 0;
297 1.4 rillig break;
298 1.4 rillig case 2: /* ^B */
299 1.4 rillig case KEY_LEFT:
300 1.4 rillig if (curx > 0) {
301 1.4 rillig curx--;
302 1.4 rillig } else if (cury > 0) {
303 1.4 rillig cury--;
304 1.9 rillig curx = (int)strlen(lines.v[cury]);
305 1.4 rillig }
306 1.4 rillig break;
307 1.4 rillig case 5: /* ^E */
308 1.4 rillig case KEY_END:
309 1.9 rillig curx = (int)strlen(lines.v[cury]);
310 1.4 rillig break;
311 1.4 rillig case 6: /* ^F */
312 1.4 rillig case KEY_RIGHT:
313 1.9 rillig if (curx < (int)strlen(lines.v[cury])) {
314 1.4 rillig curx++;
315 1.9 rillig } else if (cury < (int)lines.num - 1) {
316 1.4 rillig cury++;
317 1.4 rillig curx = 0;
318 1.4 rillig }
319 1.4 rillig break;
320 1.4 rillig case 12: /* ^L */
321 1.4 rillig clear();
322 1.4 rillig break;
323 1.4 rillig case 14: /* ^N */
324 1.4 rillig case KEY_DOWN:
325 1.9 rillig if (cury < (int)lines.num - 1) {
326 1.4 rillig cury++;
327 1.4 rillig }
328 1.9 rillig if (curx > (int)strlen(lines.v[cury])) {
329 1.9 rillig curx = (int)strlen(lines.v[cury]);
330 1.4 rillig }
331 1.4 rillig if (scrolldown < cury - (LINES - 2)) {
332 1.4 rillig scrolldown = cury - (LINES - 2);
333 1.4 rillig }
334 1.4 rillig break;
335 1.4 rillig case 16: /* ^P */
336 1.4 rillig case KEY_UP:
337 1.4 rillig if (cury > 0) {
338 1.4 rillig cury--;
339 1.4 rillig }
340 1.9 rillig if (curx > (int)strlen(lines.v[cury])) {
341 1.9 rillig curx = (int)strlen(lines.v[cury]);
342 1.4 rillig }
343 1.4 rillig if (scrolldown > cury) {
344 1.4 rillig scrolldown = cury;
345 1.4 rillig }
346 1.4 rillig break;
347 1.4 rillig case '*':
348 1.4 rillig hinting = !hinting;
349 1.4 rillig break;
350 1.4 rillig case '~':
351 1.4 rillig done = true;
352 1.4 rillig break;
353 1.4 rillig default:
354 1.9 rillig if (isascii(ch) && ch_isalpha((char)ch)) {
355 1.9 rillig if (substitute((char)ch)) {
356 1.9 rillig if (curx < (int)strlen(lines.v[cury])) {
357 1.4 rillig curx++;
358 1.4 rillig }
359 1.9 rillig if (curx == (int)strlen(lines.v[cury]) &&
360 1.9 rillig cury < (int)lines.num - 1) {
361 1.4 rillig curx = 0;
362 1.4 rillig cury++;
363 1.4 rillig }
364 1.4 rillig }
365 1.9 rillig } else if (curx < (int)strlen(lines.v[cury]) &&
366 1.4 rillig ch == lines.v[cury][curx]) {
367 1.4 rillig curx++;
368 1.9 rillig if (curx == (int)strlen(lines.v[cury]) &&
369 1.9 rillig cury < (int)lines.num - 1) {
370 1.4 rillig curx = 0;
371 1.4 rillig cury++;
372 1.4 rillig }
373 1.4 rillig } else {
374 1.4 rillig beep();
375 1.4 rillig }
376 1.4 rillig break;
377 1.4 rillig }
378 1.4 rillig }
379 1.1 dholland }
380 1.1 dholland
381 1.1 dholland ////////////////////////////////////////////////////////////
382 1.1 dholland
383 1.4 rillig int
384 1.4 rillig main(void)
385 1.4 rillig {
386 1.4 rillig
387 1.4 rillig stringarray_init(&lines);
388 1.4 rillig stringarray_init(&sollines);
389 1.9 rillig srandom((unsigned int)time(NULL));
390 1.4 rillig readquote();
391 1.4 rillig encode();
392 1.4 rillig opencurses();
393 1.4 rillig
394 1.6 rillig keypad(stdscr, true);
395 1.4 rillig loop();
396 1.4 rillig
397 1.4 rillig closecurses();
398 1.4 rillig stringarray_cleanup(&sollines);
399 1.4 rillig stringarray_cleanup(&lines);
400 1.1 dholland }
401