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