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