1 /* $NetBSD: quiz.c,v 1.9 1995/04/22 10:16:58 cgd Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jim R. Oldroyd at The Instruction Set and Keith Gabryelski at 9 * Commodore Business Machines. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40 #ifndef lint 41 static char copyright[] = 42 "@(#) Copyright (c) 1991, 1993\n\ 43 The Regents of the University of California. All rights reserved.\n"; 44 #endif /* not lint */ 45 46 #ifndef lint 47 #if 0 48 static char sccsid[] = "@(#)quiz.c 8.2 (Berkeley) 1/3/94"; 49 #else 50 static char rcsid[] = "$NetBSD: quiz.c,v 1.9 1995/04/22 10:16:58 cgd Exp $"; 51 #endif 52 #endif /* not lint */ 53 54 #include <sys/types.h> 55 #include <errno.h> 56 #include <time.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <ctype.h> 61 #include <err.h> 62 #include "quiz.h" 63 #include "pathnames.h" 64 65 static QE qlist; 66 static int catone, cattwo, tflag; 67 static u_int qsize; 68 69 char *appdstr __P((char *, char *, size_t)); 70 void downcase __P((char *)); 71 void get_cats __P((char *, char *)); 72 void get_file __P((char *)); 73 char *next_cat __P((char *)); 74 void quiz __P((void)); 75 void score __P((u_int, u_int, u_int)); 76 void show_index __P((void)); 77 void usage __P((void)); 78 79 int 80 main(argc, argv) 81 int argc; 82 char *argv[]; 83 { 84 register int ch; 85 char *indexfile; 86 87 indexfile = _PATH_QUIZIDX; 88 while ((ch = getopt(argc, argv, "i:t")) != EOF) 89 switch(ch) { 90 case 'i': 91 indexfile = optarg; 92 break; 93 case 't': 94 tflag = 1; 95 break; 96 case '?': 97 default: 98 usage(); 99 } 100 argc -= optind; 101 argv += optind; 102 103 switch(argc) { 104 case 0: 105 get_file(indexfile); 106 show_index(); 107 break; 108 case 2: 109 get_file(indexfile); 110 get_cats(argv[0], argv[1]); 111 quiz(); 112 break; 113 default: 114 usage(); 115 } 116 exit(0); 117 } 118 119 void 120 get_file(file) 121 char *file; 122 { 123 register FILE *fp; 124 register QE *qp; 125 size_t len; 126 char *lp; 127 128 if ((fp = fopen(file, "r")) == NULL) 129 err(1, "%s", file); 130 131 /* 132 * XXX 133 * Should really free up space from any earlier read list 134 * but there are no reverse pointers to do so with. 135 */ 136 qp = &qlist; 137 qsize = 0; 138 while ((lp = fgetln(fp, &len)) != NULL) { 139 lp[--len] = '\0'; 140 if (qp->q_text && qp->q_text[strlen(qp->q_text) - 1] == '\\') 141 qp->q_text = appdstr(qp->q_text, lp, len); 142 else { 143 if ((qp->q_next = malloc(sizeof(QE))) == NULL) 144 err(1, NULL); 145 qp = qp->q_next; 146 if ((qp->q_text = strdup(lp)) == NULL) 147 err(1, NULL); 148 qp->q_asked = qp->q_answered = FALSE; 149 qp->q_next = NULL; 150 ++qsize; 151 } 152 } 153 (void)fclose(fp); 154 } 155 156 void 157 show_index() 158 { 159 register QE *qp; 160 register char *p, *s; 161 FILE *pf; 162 163 if ((pf = popen(_PATH_PAGER, "w")) == NULL) 164 err(1, "%s", _PATH_PAGER); 165 (void)fprintf(pf, "Subjects:\n\n"); 166 for (qp = qlist.q_next; qp; qp = qp->q_next) { 167 for (s = next_cat(qp->q_text); s; s = next_cat(s)) { 168 if (!rxp_compile(s)) 169 errx(1, "%s", rxperr); 170 if (p = rxp_expand()) 171 (void)fprintf(pf, "%s ", p); 172 } 173 (void)fprintf(pf, "\n"); 174 } 175 (void)fprintf(pf, "\n%s\n%s\n%s\n", 176 "For example, \"quiz victim killer\" prints a victim's name and you reply", 177 "with the killer, and \"quiz killer victim\" works the other way around.", 178 "Type an empty line to get the correct answer."); 179 (void)pclose(pf); 180 } 181 182 void 183 get_cats(cat1, cat2) 184 char *cat1, *cat2; 185 { 186 register QE *qp; 187 int i; 188 char *s; 189 190 downcase(cat1); 191 downcase(cat2); 192 for (qp = qlist.q_next; qp; qp = qp->q_next) { 193 s = next_cat(qp->q_text); 194 catone = cattwo = i = 0; 195 while (s) { 196 if (!rxp_compile(s)) 197 errx(1, "%s", rxperr); 198 i++; 199 if (rxp_match(cat1)) 200 catone = i; 201 if (rxp_match(cat2)) 202 cattwo = i; 203 s = next_cat(s); 204 } 205 if (catone && cattwo && catone != cattwo) { 206 if (!rxp_compile(qp->q_text)) 207 errx(1, "%s", rxperr); 208 get_file(rxp_expand()); 209 return; 210 } 211 } 212 errx(1, "invalid categories"); 213 } 214 215 void 216 quiz() 217 { 218 register QE *qp; 219 register int i; 220 size_t len; 221 u_int guesses, rights, wrongs; 222 int next; 223 char *answer, *s, *t, question[LINE_SZ]; 224 225 srandom(time(NULL)); 226 guesses = rights = wrongs = 0; 227 for (;;) { 228 if (qsize == 0) 229 break; 230 next = random() % qsize; 231 qp = qlist.q_next; 232 for (i = 0; i < next; i++) 233 qp = qp->q_next; 234 while (qp && qp->q_answered) 235 qp = qp->q_next; 236 if (!qp) { 237 qsize = next; 238 continue; 239 } 240 if (tflag && random() % 100 > 20) { 241 /* repeat questions in tutorial mode */ 242 while (qp && (!qp->q_asked || qp->q_answered)) 243 qp = qp->q_next; 244 if (!qp) 245 continue; 246 } 247 s = qp->q_text; 248 for (i = 0; i < catone - 1; i++) 249 s = next_cat(s); 250 if (!rxp_compile(s)) 251 errx(1, "%s", rxperr); 252 t = rxp_expand(); 253 if (!t || *t == '\0') { 254 qp->q_answered = TRUE; 255 continue; 256 } 257 (void)strcpy(question, t); 258 s = qp->q_text; 259 for (i = 0; i < cattwo - 1; i++) 260 s = next_cat(s); 261 if (!rxp_compile(s)) 262 errx(1, "%s", rxperr); 263 t = rxp_expand(); 264 if (!t || *t == '\0') { 265 qp->q_answered = TRUE; 266 continue; 267 } 268 qp->q_asked = TRUE; 269 (void)printf("%s?\n", question); 270 for (;; ++guesses) { 271 if ((answer = fgetln(stdin, &len)) == NULL) { 272 score(rights, wrongs, guesses); 273 exit(0); 274 } 275 answer[len - 1] = '\0'; 276 downcase(answer); 277 if (rxp_match(answer)) { 278 (void)printf("Right!\n"); 279 ++rights; 280 qp->q_answered = TRUE; 281 break; 282 } 283 if (*answer == '\0') { 284 (void)printf("%s\n", t); 285 ++wrongs; 286 if (!tflag) 287 qp->q_answered = TRUE; 288 break; 289 } 290 (void)printf("What?\n"); 291 } 292 } 293 score(rights, wrongs, guesses); 294 } 295 296 char * 297 next_cat(s) 298 register char * s; 299 { 300 for (;;) 301 switch (*s++) { 302 case '\0': 303 return (NULL); 304 case '\\': 305 break; 306 case ':': 307 return (s); 308 } 309 /* NOTREACHED */ 310 } 311 312 char * 313 appdstr(s, tp, len) 314 char *s; 315 register char *tp; 316 size_t len; 317 { 318 register char *mp, *sp; 319 register int ch; 320 char *m; 321 322 if ((m = malloc(strlen(s) + len + 1)) == NULL) 323 err(1, NULL); 324 for (mp = m, sp = s; *mp++ = *sp++;); 325 --mp; 326 if (*(mp - 1) == '\\') 327 --mp; 328 329 while ((ch = *mp++ = *tp++) && ch != '\n'); 330 *mp = '\0'; 331 332 free(s); 333 return (m); 334 } 335 336 void 337 score(r, w, g) 338 u_int r, w, g; 339 { 340 (void)printf("Rights %d, wrongs %d,", r, w); 341 if (g) 342 (void)printf(" extra guesses %d,", g); 343 (void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0); 344 } 345 346 void 347 downcase(p) 348 register char *p; 349 { 350 register int ch; 351 352 for (; ch = *p; ++p) 353 if (isascii(ch) && isupper(ch)) 354 *p = tolower(ch); 355 } 356 357 void 358 usage() 359 { 360 (void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n"); 361 exit(1); 362 } 363