147862Sbostic /*- 2*51609Sbostic * Copyright (c) 1991 The Regents of the University of California. 3*51609Sbostic * All rights reserved. 4*51609Sbostic * 5*51609Sbostic * This code is derived from software contributed to Berkeley by 6*51609Sbostic * Jim R. Oldroyd at The Instruction Set. 7*51609Sbostic * 8*51609Sbostic * %sccs.include.redist.c% 947862Sbostic */ 108867Smckusick 1147862Sbostic #ifndef lint 12*51609Sbostic char copyright[] = 13*51609Sbostic "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 14*51609Sbostic All rights reserved.\n"; 1547862Sbostic #endif /* not lint */ 168867Smckusick 17*51609Sbostic #ifndef lint 18*51609Sbostic static char sccsid[] = "@(#)quiz.c 5.1 (Berkeley) 11/10/91"; 19*51609Sbostic #endif /* not lint */ 20*51609Sbostic 21*51609Sbostic #include <sys/types.h> 22*51609Sbostic #include <errno.h> 23*51609Sbostic #include <time.h> 248867Smckusick #include <stdio.h> 25*51609Sbostic #include <stdlib.h> 26*51609Sbostic #include <string.h> 27*51609Sbostic #include <ctype.h> 28*51609Sbostic #include "quiz.h" 2941774Sbostic #include "pathnames.h" 3041774Sbostic 31*51609Sbostic static QE qlist; 32*51609Sbostic static int catone, cattwo, tflag; 33*51609Sbostic static u_int qsize; 348867Smckusick 35*51609Sbostic char *appdstr __P((char *, char *)); 36*51609Sbostic void downcase __P((char *)); 37*51609Sbostic void err __P((const char *, ...)); 38*51609Sbostic void get_cats __P((char *, char *)); 39*51609Sbostic void get_file __P((char *)); 40*51609Sbostic char *next_cat __P((char *)); 41*51609Sbostic void quiz __P((void)); 42*51609Sbostic void score __P((u_int, u_int, u_int)); 43*51609Sbostic void show_index __P((void)); 44*51609Sbostic void usage __P((void)); 458867Smckusick 46*51609Sbostic int 47*51609Sbostic main(argc, argv) 48*51609Sbostic int argc; 49*51609Sbostic char *argv[]; 508867Smckusick { 5133196Sbostic register int ch; 52*51609Sbostic char *indexfile; 538867Smckusick 54*51609Sbostic indexfile = _PATH_QUIZIDX; 55*51609Sbostic while ((ch = getopt(argc, argv, "i:t")) != EOF) 56*51609Sbostic switch(ch) { 57*51609Sbostic case 'i': 58*51609Sbostic indexfile = optarg; 59*51609Sbostic break; 60*51609Sbostic case 't': 61*51609Sbostic tflag = 1; 62*51609Sbostic break; 63*51609Sbostic case '?': 648867Smckusick default: 65*51609Sbostic usage(); 668867Smckusick } 67*51609Sbostic argc -= optind; 68*51609Sbostic argv += optind; 698867Smckusick 70*51609Sbostic switch(argc) { 71*51609Sbostic case 0: 72*51609Sbostic get_file(indexfile); 73*51609Sbostic show_index(); 74*51609Sbostic break; 75*51609Sbostic case 2: 76*51609Sbostic get_file(indexfile); 77*51609Sbostic get_cats(argv[0], argv[1]); 78*51609Sbostic quiz(); 79*51609Sbostic break; 80*51609Sbostic default: 81*51609Sbostic usage(); 828867Smckusick } 83*51609Sbostic exit(0); 848867Smckusick } 858867Smckusick 86*51609Sbostic void 87*51609Sbostic get_file(file) 88*51609Sbostic char *file; 898867Smckusick { 90*51609Sbostic register FILE *fp; 91*51609Sbostic register QE *qp; 92*51609Sbostic size_t len; 93*51609Sbostic char *lp; 948867Smckusick 95*51609Sbostic if ((fp = fopen(file, "r")) == NULL) 96*51609Sbostic err("%s: %s", file, strerror(errno)); 978867Smckusick 98*51609Sbostic /* 99*51609Sbostic * XXX 100*51609Sbostic * Should really free up space from any earlier read list 101*51609Sbostic * but there are no reverse pointers to do so with. 102*51609Sbostic */ 103*51609Sbostic qp = &qlist; 104*51609Sbostic qsize = 0; 105*51609Sbostic while ((lp = fgetline(fp, &len)) != NULL) { 106*51609Sbostic if (qp->q_text && qp->q_text[strlen(qp->q_text) - 1] == '\\') 107*51609Sbostic qp->q_text = appdstr(qp->q_text, lp); 108*51609Sbostic else { 109*51609Sbostic if ((qp->q_next = malloc(sizeof(QE))) == NULL) 110*51609Sbostic err("%s", strerror(errno)); 111*51609Sbostic qp = qp->q_next; 112*51609Sbostic if ((qp->q_text = strdup(lp)) == NULL) 113*51609Sbostic err("%s", strerror(errno)); 114*51609Sbostic qp->q_asked = qp->q_answered = FALSE; 115*51609Sbostic qp->q_next = NULL; 116*51609Sbostic ++qsize; 1178867Smckusick } 1188867Smckusick } 119*51609Sbostic (void)fclose(fp); 1208867Smckusick } 1218867Smckusick 122*51609Sbostic void 123*51609Sbostic show_index() 1248867Smckusick { 125*51609Sbostic register QE *qp; 126*51609Sbostic register char *p, *s; 127*51609Sbostic FILE *pf; 1288867Smckusick 129*51609Sbostic if ((pf = popen(_PATH_PAGER, "w")) == NULL) 130*51609Sbostic err("%s: %s", _PATH_PAGER, strerror(errno)); 131*51609Sbostic (void)fprintf(pf, "Subjects:\n\n"); 132*51609Sbostic for (qp = qlist.q_next; qp; qp = qp->q_next) { 133*51609Sbostic for (s = next_cat(qp->q_text); s; s = next_cat(s)) { 134*51609Sbostic if (!rxp_compile(s)) 135*51609Sbostic err("%s", rxperr); 136*51609Sbostic if (p = rxp_expand()) 137*51609Sbostic (void)fprintf(pf, "%s ", p); 1388867Smckusick } 139*51609Sbostic (void)fprintf(pf, "\n"); 1408867Smckusick } 141*51609Sbostic (void)fprintf(pf, "\n%s\n%s\n%s\n", 142*51609Sbostic "For example, \"quiz victim killer\" prints a victim's name and you reply", 143*51609Sbostic "with the killer, and \"quiz killer victim\" works the other way around.", 144*51609Sbostic "Type an empty line to get the correct answer."); 145*51609Sbostic (void)pclose(pf); 1468867Smckusick } 1478867Smckusick 148*51609Sbostic void 149*51609Sbostic get_cats(cat1, cat2) 150*51609Sbostic char *cat1, *cat2; 1518867Smckusick { 152*51609Sbostic register QE *qp; 153*51609Sbostic int i; 154*51609Sbostic char *s; 155*51609Sbostic 156*51609Sbostic downcase(cat1); 157*51609Sbostic downcase(cat2); 158*51609Sbostic for (qp = qlist.q_next; qp; qp = qp->q_next) { 159*51609Sbostic s = next_cat(qp->q_text); 160*51609Sbostic catone = cattwo = i = 0; 161*51609Sbostic while (s) { 162*51609Sbostic if (!rxp_compile(s)) 163*51609Sbostic err("%s", rxperr); 164*51609Sbostic i++; 165*51609Sbostic if (rxp_match(cat1)) 166*51609Sbostic catone = i; 167*51609Sbostic if (rxp_match(cat2)) 168*51609Sbostic cattwo = i; 169*51609Sbostic s = next_cat(s); 1708867Smckusick } 171*51609Sbostic if (catone && cattwo && catone != cattwo) { 172*51609Sbostic if (!rxp_compile(qp->q_text)) 173*51609Sbostic err("%s", rxperr); 174*51609Sbostic get_file(rxp_expand()); 175*51609Sbostic return; 176*51609Sbostic } 1778867Smckusick } 178*51609Sbostic err("invalid categories"); 1798867Smckusick } 1808867Smckusick 181*51609Sbostic void 182*51609Sbostic quiz() 1838867Smckusick { 184*51609Sbostic register QE *qp; 185*51609Sbostic register int i; 186*51609Sbostic u_int guesses, rights, wrongs; 187*51609Sbostic int next; 188*51609Sbostic char *s, *t, question[LINE_SZ]; 189*51609Sbostic char *answer; 1908867Smckusick 191*51609Sbostic srandom(time(NULL)); 192*51609Sbostic guesses = rights = wrongs = 0; 193*51609Sbostic for (;;) { 194*51609Sbostic if (qsize == 0) 1958867Smckusick break; 196*51609Sbostic next = random() % qsize; 197*51609Sbostic qp = qlist.q_next; 198*51609Sbostic for (i = 0; i < next; i++) 199*51609Sbostic qp = qp->q_next; 200*51609Sbostic while (qp && qp->q_answered) 201*51609Sbostic qp = qp->q_next; 202*51609Sbostic if (!qp) { 203*51609Sbostic qsize = next; 204*51609Sbostic continue; 2058867Smckusick } 206*51609Sbostic if (tflag && random() % 100 > 20) { 207*51609Sbostic /* repeat questions in tutorial mode */ 208*51609Sbostic while (qp && (!qp->q_asked || qp->q_answered)) 209*51609Sbostic qp = qp->q_next; 210*51609Sbostic if (!qp) 211*51609Sbostic continue; 2128867Smckusick } 213*51609Sbostic s = qp->q_text; 214*51609Sbostic for (i = 0; i < catone - 1; i++) 215*51609Sbostic s = next_cat(s); 216*51609Sbostic if (!rxp_compile(s)) 217*51609Sbostic err("%s", rxperr); 218*51609Sbostic t = rxp_expand(); 219*51609Sbostic if (!t || *t == '\0') { 220*51609Sbostic qp->q_answered = TRUE; 2218867Smckusick continue; 2228867Smckusick } 223*51609Sbostic (void)strcpy(question, t); 224*51609Sbostic s = qp->q_text; 225*51609Sbostic for (i = 0; i < cattwo - 1; i++) 226*51609Sbostic s = next_cat(s); 227*51609Sbostic if (!rxp_compile(s)) 228*51609Sbostic err("%s", rxperr); 229*51609Sbostic t = rxp_expand(); 230*51609Sbostic if (!t || *t == '\0') { 231*51609Sbostic qp->q_answered = TRUE; 232*51609Sbostic continue; 233*51609Sbostic } 234*51609Sbostic qp->q_asked = TRUE; 235*51609Sbostic (void)printf("%s?\n", question); 236*51609Sbostic for (;; ++guesses) { 237*51609Sbostic if ((answer = fgetline(stdin, NULL)) == NULL) { 238*51609Sbostic score(rights, wrongs, guesses); 239*51609Sbostic exit(0); 240*51609Sbostic } 241*51609Sbostic downcase(answer); 242*51609Sbostic if (rxp_match(answer)) { 243*51609Sbostic (void)printf("Right!\n"); 244*51609Sbostic ++rights; 245*51609Sbostic qp->q_answered = TRUE; 2468867Smckusick break; 2478867Smckusick } 248*51609Sbostic if (*answer == '\0') { 249*51609Sbostic (void)printf("%s\n", t); 250*51609Sbostic ++wrongs; 251*51609Sbostic if (!tflag) 252*51609Sbostic qp->q_answered = TRUE; 2538867Smckusick break; 2548867Smckusick } 255*51609Sbostic (void)printf("What?\n"); 2568867Smckusick } 2578867Smckusick } 258*51609Sbostic score(rights, wrongs, guesses); 2598867Smckusick } 2608867Smckusick 261*51609Sbostic char * 262*51609Sbostic next_cat(s) 263*51609Sbostic register char * s; 2648867Smckusick { 265*51609Sbostic for (;;) 266*51609Sbostic switch (*s++) { 267*51609Sbostic case '\0': 268*51609Sbostic return (NULL); 269*51609Sbostic case '\\': 2708867Smckusick break; 271*51609Sbostic case ':': 272*51609Sbostic return (s); 2738867Smckusick } 274*51609Sbostic /* NOTREACHED */ 2758867Smckusick } 2768867Smckusick 277*51609Sbostic char * 278*51609Sbostic appdstr(s, tp) 279*51609Sbostic char *s; 280*51609Sbostic register char *tp; 2818867Smckusick { 282*51609Sbostic register char *mp, *sp; 283*51609Sbostic register int ch; 284*51609Sbostic char *m; 285*51609Sbostic 286*51609Sbostic if ((m = malloc(strlen(sp) + strlen(tp) + 1)) == NULL) 287*51609Sbostic err("%s", strerror(errno)); 288*51609Sbostic for (mp = m, sp = s; *mp++ = *sp++;); 289*51609Sbostic 290*51609Sbostic if (*(mp - 1) == '\\') 291*51609Sbostic --mp; 292*51609Sbostic while ((ch = *mp++ = *tp++) && ch != '\n'); 293*51609Sbostic *mp = '\0'; 294*51609Sbostic 295*51609Sbostic free(s); 296*51609Sbostic return (m); 2978867Smckusick } 2988867Smckusick 29946755Sbostic void 300*51609Sbostic score(r, w, g) 301*51609Sbostic u_int r, w, g; 3028867Smckusick { 303*51609Sbostic (void)printf("Rights %d, wrongs %d,", r, w); 304*51609Sbostic if (g) 305*51609Sbostic (void)printf(" extra guesses %d,", g); 306*51609Sbostic (void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0); 3078867Smckusick } 308*51609Sbostic 309*51609Sbostic void 310*51609Sbostic downcase(p) 311*51609Sbostic register char *p; 3128867Smckusick { 313*51609Sbostic register int ch; 314*51609Sbostic 315*51609Sbostic for (; ch = *p; ++p) 316*51609Sbostic if (isascii(ch) && isupper(ch)) 317*51609Sbostic *p = tolower(ch); 3188867Smckusick } 3198867Smckusick 320*51609Sbostic void 321*51609Sbostic usage() 322*51609Sbostic { 323*51609Sbostic (void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n"); 324*51609Sbostic exit(1); 3258867Smckusick } 3268867Smckusick 327*51609Sbostic #if __STDC__ 328*51609Sbostic #include <stdarg.h> 329*51609Sbostic #else 330*51609Sbostic #include <varargs.h> 331*51609Sbostic #endif 332*51609Sbostic 333*51609Sbostic void 334*51609Sbostic #if __STDC__ 335*51609Sbostic err(const char *fmt, ...) 336*51609Sbostic #else 337*51609Sbostic err(fmt, va_alist) 338*51609Sbostic char *fmt; 339*51609Sbostic va_dcl 340*51609Sbostic #endif 3418867Smckusick { 342*51609Sbostic va_list ap; 343*51609Sbostic #if __STDC__ 344*51609Sbostic va_start(ap, fmt); 345*51609Sbostic #else 346*51609Sbostic va_start(ap); 347*51609Sbostic #endif 348*51609Sbostic (void)fprintf(stderr, "quiz: "); 349*51609Sbostic (void)vfprintf(stderr, fmt, ap); 350*51609Sbostic va_end(ap); 351*51609Sbostic (void)fprintf(stderr, "\n"); 352*51609Sbostic exit(1); 3538867Smckusick } 354