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