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 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 char copyright[] = 13 "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 14 All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)quiz.c 5.1 (Berkeley) 11/10/91"; 19 #endif /* not lint */ 20 21 #include <sys/types.h> 22 #include <errno.h> 23 #include <time.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <ctype.h> 28 #include "quiz.h" 29 #include "pathnames.h" 30 31 static QE qlist; 32 static int catone, cattwo, tflag; 33 static u_int qsize; 34 35 char *appdstr __P((char *, char *)); 36 void downcase __P((char *)); 37 void err __P((const char *, ...)); 38 void get_cats __P((char *, char *)); 39 void get_file __P((char *)); 40 char *next_cat __P((char *)); 41 void quiz __P((void)); 42 void score __P((u_int, u_int, u_int)); 43 void show_index __P((void)); 44 void usage __P((void)); 45 46 int 47 main(argc, argv) 48 int argc; 49 char *argv[]; 50 { 51 register int ch; 52 char *indexfile; 53 54 indexfile = _PATH_QUIZIDX; 55 while ((ch = getopt(argc, argv, "i:t")) != EOF) 56 switch(ch) { 57 case 'i': 58 indexfile = optarg; 59 break; 60 case 't': 61 tflag = 1; 62 break; 63 case '?': 64 default: 65 usage(); 66 } 67 argc -= optind; 68 argv += optind; 69 70 switch(argc) { 71 case 0: 72 get_file(indexfile); 73 show_index(); 74 break; 75 case 2: 76 get_file(indexfile); 77 get_cats(argv[0], argv[1]); 78 quiz(); 79 break; 80 default: 81 usage(); 82 } 83 exit(0); 84 } 85 86 void 87 get_file(file) 88 char *file; 89 { 90 register FILE *fp; 91 register QE *qp; 92 size_t len; 93 char *lp; 94 95 if ((fp = fopen(file, "r")) == NULL) 96 err("%s: %s", file, strerror(errno)); 97 98 /* 99 * XXX 100 * Should really free up space from any earlier read list 101 * but there are no reverse pointers to do so with. 102 */ 103 qp = &qlist; 104 qsize = 0; 105 while ((lp = fgetline(fp, &len)) != NULL) { 106 if (qp->q_text && qp->q_text[strlen(qp->q_text) - 1] == '\\') 107 qp->q_text = appdstr(qp->q_text, lp); 108 else { 109 if ((qp->q_next = malloc(sizeof(QE))) == NULL) 110 err("%s", strerror(errno)); 111 qp = qp->q_next; 112 if ((qp->q_text = strdup(lp)) == NULL) 113 err("%s", strerror(errno)); 114 qp->q_asked = qp->q_answered = FALSE; 115 qp->q_next = NULL; 116 ++qsize; 117 } 118 } 119 (void)fclose(fp); 120 } 121 122 void 123 show_index() 124 { 125 register QE *qp; 126 register char *p, *s; 127 FILE *pf; 128 129 if ((pf = popen(_PATH_PAGER, "w")) == NULL) 130 err("%s: %s", _PATH_PAGER, strerror(errno)); 131 (void)fprintf(pf, "Subjects:\n\n"); 132 for (qp = qlist.q_next; qp; qp = qp->q_next) { 133 for (s = next_cat(qp->q_text); s; s = next_cat(s)) { 134 if (!rxp_compile(s)) 135 err("%s", rxperr); 136 if (p = rxp_expand()) 137 (void)fprintf(pf, "%s ", p); 138 } 139 (void)fprintf(pf, "\n"); 140 } 141 (void)fprintf(pf, "\n%s\n%s\n%s\n", 142 "For example, \"quiz victim killer\" prints a victim's name and you reply", 143 "with the killer, and \"quiz killer victim\" works the other way around.", 144 "Type an empty line to get the correct answer."); 145 (void)pclose(pf); 146 } 147 148 void 149 get_cats(cat1, cat2) 150 char *cat1, *cat2; 151 { 152 register QE *qp; 153 int i; 154 char *s; 155 156 downcase(cat1); 157 downcase(cat2); 158 for (qp = qlist.q_next; qp; qp = qp->q_next) { 159 s = next_cat(qp->q_text); 160 catone = cattwo = i = 0; 161 while (s) { 162 if (!rxp_compile(s)) 163 err("%s", rxperr); 164 i++; 165 if (rxp_match(cat1)) 166 catone = i; 167 if (rxp_match(cat2)) 168 cattwo = i; 169 s = next_cat(s); 170 } 171 if (catone && cattwo && catone != cattwo) { 172 if (!rxp_compile(qp->q_text)) 173 err("%s", rxperr); 174 get_file(rxp_expand()); 175 return; 176 } 177 } 178 err("invalid categories"); 179 } 180 181 void 182 quiz() 183 { 184 register QE *qp; 185 register int i; 186 u_int guesses, rights, wrongs; 187 int next; 188 char *s, *t, question[LINE_SZ]; 189 char *answer; 190 191 srandom(time(NULL)); 192 guesses = rights = wrongs = 0; 193 for (;;) { 194 if (qsize == 0) 195 break; 196 next = random() % qsize; 197 qp = qlist.q_next; 198 for (i = 0; i < next; i++) 199 qp = qp->q_next; 200 while (qp && qp->q_answered) 201 qp = qp->q_next; 202 if (!qp) { 203 qsize = next; 204 continue; 205 } 206 if (tflag && random() % 100 > 20) { 207 /* repeat questions in tutorial mode */ 208 while (qp && (!qp->q_asked || qp->q_answered)) 209 qp = qp->q_next; 210 if (!qp) 211 continue; 212 } 213 s = qp->q_text; 214 for (i = 0; i < catone - 1; i++) 215 s = next_cat(s); 216 if (!rxp_compile(s)) 217 err("%s", rxperr); 218 t = rxp_expand(); 219 if (!t || *t == '\0') { 220 qp->q_answered = TRUE; 221 continue; 222 } 223 (void)strcpy(question, t); 224 s = qp->q_text; 225 for (i = 0; i < cattwo - 1; i++) 226 s = next_cat(s); 227 if (!rxp_compile(s)) 228 err("%s", rxperr); 229 t = rxp_expand(); 230 if (!t || *t == '\0') { 231 qp->q_answered = TRUE; 232 continue; 233 } 234 qp->q_asked = TRUE; 235 (void)printf("%s?\n", question); 236 for (;; ++guesses) { 237 if ((answer = fgetline(stdin, NULL)) == NULL) { 238 score(rights, wrongs, guesses); 239 exit(0); 240 } 241 downcase(answer); 242 if (rxp_match(answer)) { 243 (void)printf("Right!\n"); 244 ++rights; 245 qp->q_answered = TRUE; 246 break; 247 } 248 if (*answer == '\0') { 249 (void)printf("%s\n", t); 250 ++wrongs; 251 if (!tflag) 252 qp->q_answered = TRUE; 253 break; 254 } 255 (void)printf("What?\n"); 256 } 257 } 258 score(rights, wrongs, guesses); 259 } 260 261 char * 262 next_cat(s) 263 register char * s; 264 { 265 for (;;) 266 switch (*s++) { 267 case '\0': 268 return (NULL); 269 case '\\': 270 break; 271 case ':': 272 return (s); 273 } 274 /* NOTREACHED */ 275 } 276 277 char * 278 appdstr(s, tp) 279 char *s; 280 register char *tp; 281 { 282 register char *mp, *sp; 283 register int ch; 284 char *m; 285 286 if ((m = malloc(strlen(sp) + strlen(tp) + 1)) == NULL) 287 err("%s", strerror(errno)); 288 for (mp = m, sp = s; *mp++ = *sp++;); 289 290 if (*(mp - 1) == '\\') 291 --mp; 292 while ((ch = *mp++ = *tp++) && ch != '\n'); 293 *mp = '\0'; 294 295 free(s); 296 return (m); 297 } 298 299 void 300 score(r, w, g) 301 u_int r, w, g; 302 { 303 (void)printf("Rights %d, wrongs %d,", r, w); 304 if (g) 305 (void)printf(" extra guesses %d,", g); 306 (void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0); 307 } 308 309 void 310 downcase(p) 311 register char *p; 312 { 313 register int ch; 314 315 for (; ch = *p; ++p) 316 if (isascii(ch) && isupper(ch)) 317 *p = tolower(ch); 318 } 319 320 void 321 usage() 322 { 323 (void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n"); 324 exit(1); 325 } 326 327 #if __STDC__ 328 #include <stdarg.h> 329 #else 330 #include <varargs.h> 331 #endif 332 333 void 334 #if __STDC__ 335 err(const char *fmt, ...) 336 #else 337 err(fmt, va_alist) 338 char *fmt; 339 va_dcl 340 #endif 341 { 342 va_list ap; 343 #if __STDC__ 344 va_start(ap, fmt); 345 #else 346 va_start(ap); 347 #endif 348 (void)fprintf(stderr, "quiz: "); 349 (void)vfprintf(stderr, fmt, ap); 350 va_end(ap); 351 (void)fprintf(stderr, "\n"); 352 exit(1); 353 } 354