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 and Keith Gabryelski at 7 * Commodore Business Machines. 8 * 9 * %sccs.include.redist.c% 10 */ 11 12 #ifndef lint 13 char copyright[] = 14 "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 15 All rights reserved.\n"; 16 #endif /* not lint */ 17 18 #ifndef lint 19 static char sccsid[] = "@(#)quiz.c 5.2 (Berkeley) 01/08/92"; 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 *)); 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 = fgetline(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); 109 else { 110 if ((qp->q_next = malloc(sizeof(QE))) == NULL) 111 err("%s", strerror(errno)); 112 qp = qp->q_next; 113 if ((qp->q_text = strdup(lp)) == NULL) 114 err("%s", strerror(errno)); 115 qp->q_asked = qp->q_answered = FALSE; 116 qp->q_next = NULL; 117 ++qsize; 118 } 119 } 120 (void)fclose(fp); 121 } 122 123 void 124 show_index() 125 { 126 register QE *qp; 127 register char *p, *s; 128 FILE *pf; 129 130 if ((pf = popen(_PATH_PAGER, "w")) == NULL) 131 err("%s: %s", _PATH_PAGER, strerror(errno)); 132 (void)fprintf(pf, "Subjects:\n\n"); 133 for (qp = qlist.q_next; qp; qp = qp->q_next) { 134 for (s = next_cat(qp->q_text); s; s = next_cat(s)) { 135 if (!rxp_compile(s)) 136 err("%s", rxperr); 137 if (p = rxp_expand()) 138 (void)fprintf(pf, "%s ", p); 139 } 140 (void)fprintf(pf, "\n"); 141 } 142 (void)fprintf(pf, "\n%s\n%s\n%s\n", 143 "For example, \"quiz victim killer\" prints a victim's name and you reply", 144 "with the killer, and \"quiz killer victim\" works the other way around.", 145 "Type an empty line to get the correct answer."); 146 (void)pclose(pf); 147 } 148 149 void 150 get_cats(cat1, cat2) 151 char *cat1, *cat2; 152 { 153 register QE *qp; 154 int i; 155 char *s; 156 157 downcase(cat1); 158 downcase(cat2); 159 for (qp = qlist.q_next; qp; qp = qp->q_next) { 160 s = next_cat(qp->q_text); 161 catone = cattwo = i = 0; 162 while (s) { 163 if (!rxp_compile(s)) 164 err("%s", rxperr); 165 i++; 166 if (rxp_match(cat1)) 167 catone = i; 168 if (rxp_match(cat2)) 169 cattwo = i; 170 s = next_cat(s); 171 } 172 if (catone && cattwo && catone != cattwo) { 173 if (!rxp_compile(qp->q_text)) 174 err("%s", rxperr); 175 get_file(rxp_expand()); 176 return; 177 } 178 } 179 err("invalid categories"); 180 } 181 182 void 183 quiz() 184 { 185 register QE *qp; 186 register int i; 187 u_int guesses, rights, wrongs; 188 int next; 189 char *s, *t, question[LINE_SZ]; 190 char *answer; 191 192 srandom(time(NULL)); 193 guesses = rights = wrongs = 0; 194 for (;;) { 195 if (qsize == 0) 196 break; 197 next = random() % qsize; 198 qp = qlist.q_next; 199 for (i = 0; i < next; i++) 200 qp = qp->q_next; 201 while (qp && qp->q_answered) 202 qp = qp->q_next; 203 if (!qp) { 204 qsize = next; 205 continue; 206 } 207 if (tflag && random() % 100 > 20) { 208 /* repeat questions in tutorial mode */ 209 while (qp && (!qp->q_asked || qp->q_answered)) 210 qp = qp->q_next; 211 if (!qp) 212 continue; 213 } 214 s = qp->q_text; 215 for (i = 0; i < catone - 1; i++) 216 s = next_cat(s); 217 if (!rxp_compile(s)) 218 err("%s", rxperr); 219 t = rxp_expand(); 220 if (!t || *t == '\0') { 221 qp->q_answered = TRUE; 222 continue; 223 } 224 (void)strcpy(question, t); 225 s = qp->q_text; 226 for (i = 0; i < cattwo - 1; i++) 227 s = next_cat(s); 228 if (!rxp_compile(s)) 229 err("%s", rxperr); 230 t = rxp_expand(); 231 if (!t || *t == '\0') { 232 qp->q_answered = TRUE; 233 continue; 234 } 235 qp->q_asked = TRUE; 236 (void)printf("%s?\n", question); 237 for (;; ++guesses) { 238 if ((answer = fgetline(stdin, NULL)) == NULL) { 239 score(rights, wrongs, guesses); 240 exit(0); 241 } 242 downcase(answer); 243 if (rxp_match(answer)) { 244 (void)printf("Right!\n"); 245 ++rights; 246 qp->q_answered = TRUE; 247 break; 248 } 249 if (*answer == '\0') { 250 (void)printf("%s\n", t); 251 ++wrongs; 252 if (!tflag) 253 qp->q_answered = TRUE; 254 break; 255 } 256 (void)printf("What?\n"); 257 } 258 } 259 score(rights, wrongs, guesses); 260 } 261 262 char * 263 next_cat(s) 264 register char * s; 265 { 266 for (;;) 267 switch (*s++) { 268 case '\0': 269 return (NULL); 270 case '\\': 271 break; 272 case ':': 273 return (s); 274 } 275 /* NOTREACHED */ 276 } 277 278 char * 279 appdstr(s, tp) 280 char *s; 281 register char *tp; 282 { 283 register char *mp, *sp; 284 register int ch; 285 char *m; 286 287 if ((m = malloc(strlen(sp) + strlen(tp) + 1)) == NULL) 288 err("%s", strerror(errno)); 289 for (mp = m, sp = s; *mp++ = *sp++;); 290 291 if (*(mp - 1) == '\\') 292 --mp; 293 while ((ch = *mp++ = *tp++) && ch != '\n'); 294 *mp = '\0'; 295 296 free(s); 297 return (m); 298 } 299 300 void 301 score(r, w, g) 302 u_int r, w, g; 303 { 304 (void)printf("Rights %d, wrongs %d,", r, w); 305 if (g) 306 (void)printf(" extra guesses %d,", g); 307 (void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0); 308 } 309 310 void 311 downcase(p) 312 register char *p; 313 { 314 register int ch; 315 316 for (; ch = *p; ++p) 317 if (isascii(ch) && isupper(ch)) 318 *p = tolower(ch); 319 } 320 321 void 322 usage() 323 { 324 (void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n"); 325 exit(1); 326 } 327 328 #if __STDC__ 329 #include <stdarg.h> 330 #else 331 #include <varargs.h> 332 #endif 333 334 void 335 #if __STDC__ 336 err(const char *fmt, ...) 337 #else 338 err(fmt, va_alist) 339 char *fmt; 340 va_dcl 341 #endif 342 { 343 va_list ap; 344 #if __STDC__ 345 va_start(ap, fmt); 346 #else 347 va_start(ap); 348 #endif 349 (void)fprintf(stderr, "quiz: "); 350 (void)vfprintf(stderr, fmt, ap); 351 va_end(ap); 352 (void)fprintf(stderr, "\n"); 353 exit(1); 354 } 355