xref: /dragonfly/games/cgram/cgram.c (revision 4ed49c92)
1*4ed49c92SAaron LI /* $NetBSD: cgram.c,v 1.17 2021/02/26 15:18:40 rillig Exp $ */
2564d7699SAaron LI 
31efcf41bSAaron LI /*-
4564d7699SAaron LI  * Copyright (c) 2013, 2021 The NetBSD Foundation, Inc.
51efcf41bSAaron LI  * All rights reserved.
61efcf41bSAaron LI  *
71efcf41bSAaron LI  * This code is derived from software contributed to The NetBSD Foundation
8564d7699SAaron LI  * by David A. Holland and Roland Illig.
91efcf41bSAaron LI  *
101efcf41bSAaron LI  * Redistribution and use in source and binary forms, with or without
111efcf41bSAaron LI  * modification, are permitted provided that the following conditions
121efcf41bSAaron LI  * are met:
131efcf41bSAaron LI  * 1. Redistributions of source code must retain the above copyright
141efcf41bSAaron LI  *    notice, this list of conditions and the following disclaimer.
151efcf41bSAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
161efcf41bSAaron LI  *    notice, this list of conditions and the following disclaimer in the
171efcf41bSAaron LI  *    documentation and/or other materials provided with the distribution.
181efcf41bSAaron LI  *
191efcf41bSAaron LI  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
201efcf41bSAaron LI  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
211efcf41bSAaron LI  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
221efcf41bSAaron LI  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
231efcf41bSAaron LI  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
241efcf41bSAaron LI  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
251efcf41bSAaron LI  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
261efcf41bSAaron LI  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
271efcf41bSAaron LI  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
281efcf41bSAaron LI  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
291efcf41bSAaron LI  * POSSIBILITY OF SUCH DAMAGE.
301efcf41bSAaron LI  */
311efcf41bSAaron LI 
321efcf41bSAaron LI #include <assert.h>
33a22b106bSAaron LI #include <ctype.h>
341efcf41bSAaron LI #include <curses.h>
35564d7699SAaron LI #include <err.h>
36564d7699SAaron LI #include <stdbool.h>
37a22b106bSAaron LI #include <stdio.h>
38a22b106bSAaron LI #include <stdlib.h>
39a22b106bSAaron LI #include <string.h>
40a22b106bSAaron LI #include <time.h>
41a22b106bSAaron LI 
421efcf41bSAaron LI #include "pathnames.h"
431efcf41bSAaron LI 
441efcf41bSAaron LI ////////////////////////////////////////////////////////////
451efcf41bSAaron LI 
46564d7699SAaron LI static char
ch_toupper(char ch)47564d7699SAaron LI ch_toupper(char ch)
48a22b106bSAaron LI {
49564d7699SAaron LI 	return (char)toupper((unsigned char)ch);
50564d7699SAaron LI }
511efcf41bSAaron LI 
52564d7699SAaron LI static char
ch_tolower(char ch)53564d7699SAaron LI ch_tolower(char ch)
54564d7699SAaron LI {
55564d7699SAaron LI 	return (char)tolower((unsigned char)ch);
56564d7699SAaron LI }
57a22b106bSAaron LI 
58564d7699SAaron LI static bool
ch_isalpha(char ch)59564d7699SAaron LI ch_isalpha(char ch)
60564d7699SAaron LI {
61564d7699SAaron LI 	return isalpha((unsigned char)ch) != 0;
62564d7699SAaron LI }
63564d7699SAaron LI 
64564d7699SAaron LI static bool
ch_islower(char ch)65564d7699SAaron LI ch_islower(char ch)
66564d7699SAaron LI {
67564d7699SAaron LI 	return islower((unsigned char)ch) != 0;
68564d7699SAaron LI }
69564d7699SAaron LI 
70564d7699SAaron LI static bool
ch_isspace(char ch)71*4ed49c92SAaron LI ch_isspace(char ch)
72*4ed49c92SAaron LI {
73*4ed49c92SAaron LI 	return isspace((unsigned char)ch) != 0;
74*4ed49c92SAaron LI }
75*4ed49c92SAaron LI 
76*4ed49c92SAaron LI static bool
ch_isupper(char ch)77564d7699SAaron LI ch_isupper(char ch)
78564d7699SAaron LI {
79564d7699SAaron LI 	return isupper((unsigned char)ch) != 0;
80564d7699SAaron LI }
81564d7699SAaron LI 
82564d7699SAaron LI static int
imax(int a,int b)83564d7699SAaron LI imax(int a, int b)
84564d7699SAaron LI {
85564d7699SAaron LI 	return a > b ? a : b;
86564d7699SAaron LI }
87564d7699SAaron LI 
88564d7699SAaron LI static int
imin(int a,int b)89564d7699SAaron LI imin(int a, int b)
90564d7699SAaron LI {
91564d7699SAaron LI 	return a < b ? a : b;
921efcf41bSAaron LI }
931efcf41bSAaron LI 
941efcf41bSAaron LI ////////////////////////////////////////////////////////////
951efcf41bSAaron LI 
96564d7699SAaron LI struct string {
97564d7699SAaron LI 	char *s;
98564d7699SAaron LI 	size_t len;
99564d7699SAaron LI 	size_t cap;
1001efcf41bSAaron LI };
1011efcf41bSAaron LI 
102564d7699SAaron LI struct stringarray {
103564d7699SAaron LI 	struct string *v;
104564d7699SAaron LI 	size_t num;
105564d7699SAaron LI };
106564d7699SAaron LI 
107564d7699SAaron LI static void
string_init(struct string * s)108564d7699SAaron LI string_init(struct string *s)
109564d7699SAaron LI {
110564d7699SAaron LI 	s->s = NULL;
111564d7699SAaron LI 	s->len = 0;
112564d7699SAaron LI 	s->cap = 0;
113564d7699SAaron LI }
114564d7699SAaron LI 
115564d7699SAaron LI static void
string_add(struct string * s,char ch)116564d7699SAaron LI string_add(struct string *s, char ch)
117564d7699SAaron LI {
118564d7699SAaron LI 	if (s->len >= s->cap) {
119564d7699SAaron LI 		s->cap = 2 * s->cap + 16;
120564d7699SAaron LI 		s->s = realloc(s->s, s->cap);
121564d7699SAaron LI 		if (s->s == NULL)
122564d7699SAaron LI 			errx(1, "Out of memory");
123564d7699SAaron LI 	}
124564d7699SAaron LI 	s->s[s->len++] = ch;
125564d7699SAaron LI }
126564d7699SAaron LI 
127564d7699SAaron LI static void
string_finish(struct string * s)128564d7699SAaron LI string_finish(struct string *s)
129564d7699SAaron LI {
130564d7699SAaron LI 	string_add(s, '\0');
131564d7699SAaron LI 	s->len--;
132564d7699SAaron LI }
133564d7699SAaron LI 
134a22b106bSAaron LI static void
stringarray_init(struct stringarray * a)135a22b106bSAaron LI stringarray_init(struct stringarray *a)
136a22b106bSAaron LI {
1371efcf41bSAaron LI 	a->v = NULL;
1381efcf41bSAaron LI 	a->num = 0;
1391efcf41bSAaron LI }
1401efcf41bSAaron LI 
141a22b106bSAaron LI static void
stringarray_cleanup(struct stringarray * a)142a22b106bSAaron LI stringarray_cleanup(struct stringarray *a)
143a22b106bSAaron LI {
144564d7699SAaron LI 	for (size_t i = 0; i < a->num; i++)
145564d7699SAaron LI 		free(a->v[i].s);
1461efcf41bSAaron LI 	free(a->v);
1471efcf41bSAaron LI }
1481efcf41bSAaron LI 
149a22b106bSAaron LI static void
stringarray_add(struct stringarray * a,struct string * s)150564d7699SAaron LI stringarray_add(struct stringarray *a, struct string *s)
151a22b106bSAaron LI {
152564d7699SAaron LI 	size_t num = a->num++;
153564d7699SAaron LI 	a->v = realloc(a->v, a->num * sizeof a->v[0]);
154a22b106bSAaron LI 	if (a->v == NULL)
1551efcf41bSAaron LI 		errx(1, "Out of memory");
156564d7699SAaron LI 	a->v[num] = *s;
157564d7699SAaron LI }
158a22b106bSAaron LI 
159564d7699SAaron LI static void
stringarray_dup(struct stringarray * dst,const struct stringarray * src)160564d7699SAaron LI stringarray_dup(struct stringarray *dst, const struct stringarray *src)
161564d7699SAaron LI {
162564d7699SAaron LI 	assert(dst->num == 0);
163564d7699SAaron LI 	for (size_t i = 0; i < src->num; i++) {
164564d7699SAaron LI 		struct string str;
165564d7699SAaron LI 		string_init(&str);
166564d7699SAaron LI 		for (const char *p = src->v[i].s; *p != '\0'; p++)
167564d7699SAaron LI 			string_add(&str, *p);
168564d7699SAaron LI 		string_finish(&str);
169564d7699SAaron LI 		stringarray_add(dst, &str);
170564d7699SAaron LI 	}
1711efcf41bSAaron LI }
1721efcf41bSAaron LI 
1731efcf41bSAaron LI ////////////////////////////////////////////////////////////
1741efcf41bSAaron LI 
1751efcf41bSAaron LI static struct stringarray lines;
1761efcf41bSAaron LI static struct stringarray sollines;
1771efcf41bSAaron LI static bool hinting;
178564d7699SAaron LI static int extent_x;
179564d7699SAaron LI static int extent_y;
180564d7699SAaron LI static int offset_x;
181564d7699SAaron LI static int offset_y;
182564d7699SAaron LI static int cursor_x;
183564d7699SAaron LI static int cursor_y;
184564d7699SAaron LI 
185564d7699SAaron LI static int
cur_max_x(void)186564d7699SAaron LI cur_max_x(void)
187564d7699SAaron LI {
188*4ed49c92SAaron LI 	return (int)lines.v[cursor_y].len;
189564d7699SAaron LI }
190564d7699SAaron LI 
191564d7699SAaron LI static int
cur_max_y(void)192564d7699SAaron LI cur_max_y(void)
193564d7699SAaron LI {
194564d7699SAaron LI 	return extent_y - 1;
195564d7699SAaron LI }
1961efcf41bSAaron LI 
197*4ed49c92SAaron LI static char
char_left_of_cursor(void)198*4ed49c92SAaron LI char_left_of_cursor(void)
199*4ed49c92SAaron LI {
200*4ed49c92SAaron LI 	if (cursor_x > 0)
201*4ed49c92SAaron LI 		return lines.v[cursor_y].s[cursor_x - 1];
202*4ed49c92SAaron LI 	assert(cursor_y > 0);
203*4ed49c92SAaron LI 	return '\n'; /* eol of previous line */
204*4ed49c92SAaron LI }
205*4ed49c92SAaron LI 
206*4ed49c92SAaron LI static char
char_at_cursor(void)207*4ed49c92SAaron LI char_at_cursor(void)
208*4ed49c92SAaron LI {
209*4ed49c92SAaron LI 	if (cursor_x == cur_max_x())
210*4ed49c92SAaron LI 		return '\n';
211*4ed49c92SAaron LI 	return lines.v[cursor_y].s[cursor_x];
212*4ed49c92SAaron LI }
213*4ed49c92SAaron LI 
214a22b106bSAaron LI static void
readquote(void)215a22b106bSAaron LI readquote(void)
216a22b106bSAaron LI {
2171efcf41bSAaron LI 	FILE *f = popen(_PATH_FORTUNE, "r");
218564d7699SAaron LI 	if (f == NULL)
2191efcf41bSAaron LI 		err(1, "%s", _PATH_FORTUNE);
2201efcf41bSAaron LI 
221564d7699SAaron LI 	struct string line;
222564d7699SAaron LI 	string_init(&line);
2231efcf41bSAaron LI 
224564d7699SAaron LI 	int ch;
225564d7699SAaron LI 	while ((ch = fgetc(f)) != EOF) {
226564d7699SAaron LI 		if (ch == '\n') {
227564d7699SAaron LI 			string_finish(&line);
228564d7699SAaron LI 			stringarray_add(&lines, &line);
229564d7699SAaron LI 			string_init(&line);
230564d7699SAaron LI 		} else if (ch == '\t') {
231564d7699SAaron LI 			string_add(&line, ' ');
232564d7699SAaron LI 			while (line.len % 8 != 0)
233564d7699SAaron LI 				string_add(&line, ' ');
234564d7699SAaron LI 		} else if (ch == '\b') {
235564d7699SAaron LI 			if (line.len > 0)
236564d7699SAaron LI 				line.len--;
237a22b106bSAaron LI 		} else {
238564d7699SAaron LI 			string_add(&line, (char)ch);
2391efcf41bSAaron LI 		}
2401efcf41bSAaron LI 	}
2411efcf41bSAaron LI 
242564d7699SAaron LI 	stringarray_dup(&sollines, &lines);
243564d7699SAaron LI 
244564d7699SAaron LI 	extent_y = (int)lines.num;
245564d7699SAaron LI 	for (int i = 0; i < extent_y; i++)
246564d7699SAaron LI 		extent_x = imax(extent_x, (int)lines.v[i].len);
2471efcf41bSAaron LI 
248*4ed49c92SAaron LI 	if (pclose(f) != 0)
249*4ed49c92SAaron LI 		exit(1); /* error message must come from child process */
2501efcf41bSAaron LI }
2511efcf41bSAaron LI 
252a22b106bSAaron LI static void
encode(void)253a22b106bSAaron LI encode(void)
254a22b106bSAaron LI {
2551efcf41bSAaron LI 	int key[26];
256a22b106bSAaron LI 
257a22b106bSAaron LI 	for (int i = 0; i < 26; i++)
258a22b106bSAaron LI 		key[i] = i;
259564d7699SAaron LI 
2601efcf41bSAaron LI 	for (int i = 26; i > 1; i--) {
261564d7699SAaron LI 		int c = (int)(random() % i);
2621efcf41bSAaron LI 		int t = key[i - 1];
2631efcf41bSAaron LI 		key[i - 1] = key[c];
2641efcf41bSAaron LI 		key[c] = t;
2651efcf41bSAaron LI 	}
2661efcf41bSAaron LI 
267564d7699SAaron LI 	for (int y = 0; y < extent_y; y++) {
268564d7699SAaron LI 		for (char *p = lines.v[y].s; *p != '\0'; p++) {
269564d7699SAaron LI 			if (ch_islower(*p))
270564d7699SAaron LI 				*p = (char)('a' + key[*p - 'a']);
271564d7699SAaron LI 			if (ch_isupper(*p))
272564d7699SAaron LI 				*p = (char)('A' + key[*p - 'A']);
2731efcf41bSAaron LI 		}
2741efcf41bSAaron LI 	}
2751efcf41bSAaron LI }
2761efcf41bSAaron LI 
277*4ed49c92SAaron LI static void
substitute(char a,char b)278*4ed49c92SAaron LI substitute(char a, char b)
279a22b106bSAaron LI {
280*4ed49c92SAaron LI 	char la = ch_tolower(a);
281*4ed49c92SAaron LI 	char ua = ch_toupper(a);
282*4ed49c92SAaron LI 	char lb = ch_tolower(b);
283*4ed49c92SAaron LI 	char ub = ch_toupper(b);
2841efcf41bSAaron LI 
285564d7699SAaron LI 	for (int y = 0; y < (int)lines.num; y++) {
286564d7699SAaron LI 		for (char *p = lines.v[y].s; *p != '\0'; p++) {
287*4ed49c92SAaron LI 			if (*p == la)
288*4ed49c92SAaron LI 				*p = lb;
289*4ed49c92SAaron LI 			else if (*p == ua)
290*4ed49c92SAaron LI 				*p = ub;
291*4ed49c92SAaron LI 			else if (*p == lb)
292*4ed49c92SAaron LI 				*p = la;
293*4ed49c92SAaron LI 			else if (*p == ub)
294*4ed49c92SAaron LI 				*p = ua;
2951efcf41bSAaron LI 		}
2961efcf41bSAaron LI 	}
2971efcf41bSAaron LI }
2981efcf41bSAaron LI 
299564d7699SAaron LI static bool
is_solved(void)300564d7699SAaron LI is_solved(void)
301564d7699SAaron LI {
302564d7699SAaron LI 	for (size_t i = 0; i < lines.num; i++)
303564d7699SAaron LI 		if (strcmp(lines.v[i].s, sollines.v[i].s) != 0)
304564d7699SAaron LI 			return false;
305564d7699SAaron LI 	return true;
306564d7699SAaron LI }
307564d7699SAaron LI 
308*4ed49c92SAaron LI ////////////////////////////////////////////////////////////
309*4ed49c92SAaron LI 
310a22b106bSAaron LI static void
redraw(void)311a22b106bSAaron LI redraw(void)
312a22b106bSAaron LI {
3131efcf41bSAaron LI 	erase();
314a22b106bSAaron LI 
315564d7699SAaron LI 	int max_y = imin(LINES - 1, extent_y - offset_y);
316564d7699SAaron LI 	for (int y = 0; y < max_y; y++) {
317564d7699SAaron LI 		move(y, 0);
318d60ecdb4SAaron LI 
319564d7699SAaron LI 		int len = (int)lines.v[offset_y + y].len;
320564d7699SAaron LI 		int max_x = imin(COLS - 1, len - offset_x);
321564d7699SAaron LI 		const char *line = lines.v[offset_y + y].s;
322564d7699SAaron LI 		const char *solline = sollines.v[offset_y + y].s;
323d60ecdb4SAaron LI 
324564d7699SAaron LI 		for (int x = 0; x < max_x; x++) {
325564d7699SAaron LI 			char ch = line[offset_x + x];
326564d7699SAaron LI 			bool bold = hinting &&
327564d7699SAaron LI 			    ch == solline[offset_x + x] &&
328564d7699SAaron LI 			    ch_isalpha(ch);
329564d7699SAaron LI 
330564d7699SAaron LI 			if (bold)
331564d7699SAaron LI 				attron(A_BOLD);
332564d7699SAaron LI 			addch(ch);
333564d7699SAaron LI 			if (bold)
334564d7699SAaron LI 				attroff(A_BOLD);
3351efcf41bSAaron LI 		}
3361efcf41bSAaron LI 		clrtoeol();
3371efcf41bSAaron LI 	}
3381efcf41bSAaron LI 
3391efcf41bSAaron LI 	move(LINES - 1, 0);
3401efcf41bSAaron LI 	addstr("~ to quit, * to cheat, ^pnfb to move");
3411efcf41bSAaron LI 
342*4ed49c92SAaron LI 	if (is_solved()) {
343*4ed49c92SAaron LI 		if (extent_y + 1 - offset_y < LINES - 2)
344*4ed49c92SAaron LI 			move(extent_y + 1 - offset_y, 0);
345*4ed49c92SAaron LI 		else
346*4ed49c92SAaron LI 			addch(' ');
347*4ed49c92SAaron LI 		attron(A_BOLD | A_STANDOUT);
348*4ed49c92SAaron LI 		addstr("*solved*");
349*4ed49c92SAaron LI 		attroff(A_BOLD | A_STANDOUT);
350*4ed49c92SAaron LI 	}
351*4ed49c92SAaron LI 
352564d7699SAaron LI 	move(cursor_y - offset_y, cursor_x - offset_x);
3531efcf41bSAaron LI 
3541efcf41bSAaron LI 	refresh();
3551efcf41bSAaron LI }
3561efcf41bSAaron LI 
3571efcf41bSAaron LI ////////////////////////////////////////////////////////////
3581efcf41bSAaron LI 
359a22b106bSAaron LI static void
saturate_cursor(void)360564d7699SAaron LI saturate_cursor(void)
361a22b106bSAaron LI {
362564d7699SAaron LI 	cursor_y = imax(cursor_y, 0);
363564d7699SAaron LI 	cursor_y = imin(cursor_y, cur_max_y());
364a22b106bSAaron LI 
365564d7699SAaron LI 	assert(cursor_x >= 0);
366564d7699SAaron LI 	cursor_x = imin(cursor_x, cur_max_x());
367564d7699SAaron LI }
368a22b106bSAaron LI 
369564d7699SAaron LI static void
scroll_into_view(void)370564d7699SAaron LI scroll_into_view(void)
371564d7699SAaron LI {
372564d7699SAaron LI 	if (cursor_x < offset_x)
373564d7699SAaron LI 		offset_x = cursor_x;
374564d7699SAaron LI 	if (cursor_x > offset_x + COLS - 1)
375564d7699SAaron LI 		offset_x = cursor_x - (COLS - 1);
376564d7699SAaron LI 
377564d7699SAaron LI 	if (cursor_y < offset_y)
378564d7699SAaron LI 		offset_y = cursor_y;
379564d7699SAaron LI 	if (cursor_y > offset_y + LINES - 2)
380564d7699SAaron LI 		offset_y = cursor_y - (LINES - 2);
381564d7699SAaron LI }
382564d7699SAaron LI 
383*4ed49c92SAaron LI static bool
can_go_left(void)384*4ed49c92SAaron LI can_go_left(void)
385*4ed49c92SAaron LI {
386*4ed49c92SAaron LI 	return cursor_y > 0 ||
387*4ed49c92SAaron LI 	    (cursor_y == 0 && cursor_x > 0);
388*4ed49c92SAaron LI }
389*4ed49c92SAaron LI 
390*4ed49c92SAaron LI static bool
can_go_right(void)391*4ed49c92SAaron LI can_go_right(void)
392*4ed49c92SAaron LI {
393*4ed49c92SAaron LI 	return cursor_y < cur_max_y() ||
394*4ed49c92SAaron LI 	    (cursor_y == cur_max_y() && cursor_x < cur_max_x());
395*4ed49c92SAaron LI }
396*4ed49c92SAaron LI 
397*4ed49c92SAaron LI static void
go_to_prev_line(void)398*4ed49c92SAaron LI go_to_prev_line(void)
399*4ed49c92SAaron LI {
400*4ed49c92SAaron LI 	cursor_y--;
401*4ed49c92SAaron LI 	cursor_x = cur_max_x();
402*4ed49c92SAaron LI }
403*4ed49c92SAaron LI 
404*4ed49c92SAaron LI static void
go_to_next_line(void)405*4ed49c92SAaron LI go_to_next_line(void)
406*4ed49c92SAaron LI {
407*4ed49c92SAaron LI 	cursor_x = 0;
408*4ed49c92SAaron LI 	cursor_y++;
409*4ed49c92SAaron LI }
410*4ed49c92SAaron LI 
411*4ed49c92SAaron LI static void
go_left(void)412*4ed49c92SAaron LI go_left(void)
413*4ed49c92SAaron LI {
414*4ed49c92SAaron LI 	if (cursor_x > 0)
415*4ed49c92SAaron LI 		cursor_x--;
416*4ed49c92SAaron LI 	else if (cursor_y > 0)
417*4ed49c92SAaron LI 		go_to_prev_line();
418*4ed49c92SAaron LI }
419*4ed49c92SAaron LI 
420*4ed49c92SAaron LI static void
go_right(void)421*4ed49c92SAaron LI go_right(void)
422*4ed49c92SAaron LI {
423*4ed49c92SAaron LI 	if (cursor_x < cur_max_x())
424*4ed49c92SAaron LI 		cursor_x++;
425*4ed49c92SAaron LI 	else if (cursor_y < cur_max_y())
426*4ed49c92SAaron LI 		go_to_next_line();
427*4ed49c92SAaron LI }
428*4ed49c92SAaron LI 
429*4ed49c92SAaron LI static void
go_to_prev_word(void)430*4ed49c92SAaron LI go_to_prev_word(void)
431*4ed49c92SAaron LI {
432*4ed49c92SAaron LI 	while (can_go_left() && ch_isspace(char_left_of_cursor()))
433*4ed49c92SAaron LI 		go_left();
434*4ed49c92SAaron LI 
435*4ed49c92SAaron LI 	while (can_go_left() && !ch_isspace(char_left_of_cursor()))
436*4ed49c92SAaron LI 		go_left();
437*4ed49c92SAaron LI }
438*4ed49c92SAaron LI 
439*4ed49c92SAaron LI static void
go_to_next_word(void)440*4ed49c92SAaron LI go_to_next_word(void)
441*4ed49c92SAaron LI {
442*4ed49c92SAaron LI 	while (can_go_right() && !ch_isspace(char_at_cursor()))
443*4ed49c92SAaron LI 		go_right();
444*4ed49c92SAaron LI 
445*4ed49c92SAaron LI 	while (can_go_right() && ch_isspace(char_at_cursor()))
446*4ed49c92SAaron LI 		go_right();
447*4ed49c92SAaron LI }
448*4ed49c92SAaron LI 
449*4ed49c92SAaron LI static bool
can_substitute_here(int ch)450*4ed49c92SAaron LI can_substitute_here(int ch)
451*4ed49c92SAaron LI {
452*4ed49c92SAaron LI 	return isascii(ch) &&
453*4ed49c92SAaron LI 	    ch_isalpha((char)ch) &&
454*4ed49c92SAaron LI 	    cursor_x < cur_max_x() &&
455*4ed49c92SAaron LI 	    ch_isalpha(char_at_cursor());
456*4ed49c92SAaron LI }
457*4ed49c92SAaron LI 
458564d7699SAaron LI static void
handle_char_input(int ch)459564d7699SAaron LI handle_char_input(int ch)
460564d7699SAaron LI {
461*4ed49c92SAaron LI 	if (ch == char_at_cursor())
462*4ed49c92SAaron LI 		go_right();
463*4ed49c92SAaron LI 	else if (can_substitute_here(ch)) {
464*4ed49c92SAaron LI 		substitute(char_at_cursor(), (char)ch);
465*4ed49c92SAaron LI 		go_right();
466*4ed49c92SAaron LI 	} else
467564d7699SAaron LI 		beep();
468564d7699SAaron LI }
469564d7699SAaron LI 
470564d7699SAaron LI static bool
handle_key(void)471564d7699SAaron LI handle_key(void)
472564d7699SAaron LI {
4731efcf41bSAaron LI 	int ch = getch();
474564d7699SAaron LI 
4751efcf41bSAaron LI 	switch (ch) {
4761efcf41bSAaron LI 	case 1:			/* ^A */
477359c8e75SAaron LI 	case KEY_HOME:
478564d7699SAaron LI 		cursor_x = 0;
4791efcf41bSAaron LI 		break;
4801efcf41bSAaron LI 	case 2:			/* ^B */
4811efcf41bSAaron LI 	case KEY_LEFT:
482*4ed49c92SAaron LI 		go_left();
4831efcf41bSAaron LI 		break;
4841efcf41bSAaron LI 	case 5:			/* ^E */
4851efcf41bSAaron LI 	case KEY_END:
486564d7699SAaron LI 		cursor_x = cur_max_x();
4871efcf41bSAaron LI 		break;
4881efcf41bSAaron LI 	case 6:			/* ^F */
4891efcf41bSAaron LI 	case KEY_RIGHT:
490*4ed49c92SAaron LI 		go_right();
491*4ed49c92SAaron LI 		break;
492*4ed49c92SAaron LI 	case '\t':
493*4ed49c92SAaron LI 		go_to_next_word();
494*4ed49c92SAaron LI 		break;
495*4ed49c92SAaron LI 	case KEY_BTAB:
496*4ed49c92SAaron LI 		go_to_prev_word();
497*4ed49c92SAaron LI 		break;
498*4ed49c92SAaron LI 	case '\n':
499*4ed49c92SAaron LI 		go_to_next_line();
5001efcf41bSAaron LI 		break;
5011efcf41bSAaron LI 	case 12:		/* ^L */
5021efcf41bSAaron LI 		clear();
5031efcf41bSAaron LI 		break;
5041efcf41bSAaron LI 	case 14:		/* ^N */
5051efcf41bSAaron LI 	case KEY_DOWN:
506564d7699SAaron LI 		cursor_y++;
5071efcf41bSAaron LI 		break;
5081efcf41bSAaron LI 	case 16:		/* ^P */
5091efcf41bSAaron LI 	case KEY_UP:
510564d7699SAaron LI 		cursor_y--;
511564d7699SAaron LI 		break;
512564d7699SAaron LI 	case KEY_PPAGE:
513564d7699SAaron LI 		cursor_y -= LINES - 2;
514564d7699SAaron LI 		break;
515564d7699SAaron LI 	case KEY_NPAGE:
516564d7699SAaron LI 		cursor_y += LINES - 2;
5171efcf41bSAaron LI 		break;
5181efcf41bSAaron LI 	case '*':
5191efcf41bSAaron LI 		hinting = !hinting;
5201efcf41bSAaron LI 		break;
5211efcf41bSAaron LI 	case '~':
522564d7699SAaron LI 		return false;
523*4ed49c92SAaron LI 	case KEY_RESIZE:
524*4ed49c92SAaron LI 		break;
5251efcf41bSAaron LI 	default:
526564d7699SAaron LI 		handle_char_input(ch);
5271efcf41bSAaron LI 		break;
5281efcf41bSAaron LI 	}
529564d7699SAaron LI 	return true;
5301efcf41bSAaron LI }
531564d7699SAaron LI 
532564d7699SAaron LI static void
init(void)533564d7699SAaron LI init(void)
534564d7699SAaron LI {
535564d7699SAaron LI 	stringarray_init(&lines);
536564d7699SAaron LI 	stringarray_init(&sollines);
537564d7699SAaron LI 	srandom((unsigned int)time(NULL));
538564d7699SAaron LI 	readquote();
539564d7699SAaron LI 	encode();
540*4ed49c92SAaron LI 
541*4ed49c92SAaron LI 	initscr();
542*4ed49c92SAaron LI 	cbreak();
543*4ed49c92SAaron LI 	noecho();
544*4ed49c92SAaron LI 	keypad(stdscr, true);
545564d7699SAaron LI }
546564d7699SAaron LI 
547564d7699SAaron LI static void
loop(void)548564d7699SAaron LI loop(void)
549564d7699SAaron LI {
550564d7699SAaron LI 	for (;;) {
551564d7699SAaron LI 		redraw();
552564d7699SAaron LI 		if (!handle_key())
553564d7699SAaron LI 			break;
554564d7699SAaron LI 		saturate_cursor();
555564d7699SAaron LI 		scroll_into_view();
556564d7699SAaron LI 	}
557564d7699SAaron LI }
558564d7699SAaron LI 
559564d7699SAaron LI static void
clean_up(void)560564d7699SAaron LI clean_up(void)
561564d7699SAaron LI {
562*4ed49c92SAaron LI 	endwin();
563*4ed49c92SAaron LI 
564564d7699SAaron LI 	stringarray_cleanup(&sollines);
565564d7699SAaron LI 	stringarray_cleanup(&lines);
5661efcf41bSAaron LI }
5671efcf41bSAaron LI 
5681efcf41bSAaron LI ////////////////////////////////////////////////////////////
5691efcf41bSAaron LI 
570a22b106bSAaron LI int
main(void)571a22b106bSAaron LI main(void)
572a22b106bSAaron LI {
573564d7699SAaron LI 	init();
5741efcf41bSAaron LI 	loop();
575564d7699SAaron LI 	clean_up();
5761efcf41bSAaron LI }
577