147862Sbostic /*- 251609Sbostic * Copyright (c) 1991 The Regents of the University of California. 351609Sbostic * All rights reserved. 451609Sbostic * 551609Sbostic * This code is derived from software contributed to Berkeley by 6*52145Sbostic * Jim R. Oldroyd at The Instruction Set and Keith Gabryelski at 7*52145Sbostic * Commodore Business Machines. 851609Sbostic * 951609Sbostic * %sccs.include.redist.c% 1047862Sbostic */ 118867Smckusick 1247862Sbostic #ifndef lint 1351609Sbostic char copyright[] = 1451609Sbostic "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 1551609Sbostic All rights reserved.\n"; 1647862Sbostic #endif /* not lint */ 178867Smckusick 1851609Sbostic #ifndef lint 19*52145Sbostic static char sccsid[] = "@(#)quiz.c 5.2 (Berkeley) 01/08/92"; 2051609Sbostic #endif /* not lint */ 2151609Sbostic 2251609Sbostic #include <sys/types.h> 2351609Sbostic #include <errno.h> 2451609Sbostic #include <time.h> 258867Smckusick #include <stdio.h> 2651609Sbostic #include <stdlib.h> 2751609Sbostic #include <string.h> 2851609Sbostic #include <ctype.h> 2951609Sbostic #include "quiz.h" 3041774Sbostic #include "pathnames.h" 3141774Sbostic 3251609Sbostic static QE qlist; 3351609Sbostic static int catone, cattwo, tflag; 3451609Sbostic static u_int qsize; 358867Smckusick 3651609Sbostic char *appdstr __P((char *, char *)); 3751609Sbostic void downcase __P((char *)); 3851609Sbostic void err __P((const char *, ...)); 3951609Sbostic void get_cats __P((char *, char *)); 4051609Sbostic void get_file __P((char *)); 4151609Sbostic char *next_cat __P((char *)); 4251609Sbostic void quiz __P((void)); 4351609Sbostic void score __P((u_int, u_int, u_int)); 4451609Sbostic void show_index __P((void)); 4551609Sbostic void usage __P((void)); 468867Smckusick 4751609Sbostic int 4851609Sbostic main(argc, argv) 4951609Sbostic int argc; 5051609Sbostic char *argv[]; 518867Smckusick { 5233196Sbostic register int ch; 5351609Sbostic char *indexfile; 548867Smckusick 5551609Sbostic indexfile = _PATH_QUIZIDX; 5651609Sbostic while ((ch = getopt(argc, argv, "i:t")) != EOF) 5751609Sbostic switch(ch) { 5851609Sbostic case 'i': 5951609Sbostic indexfile = optarg; 6051609Sbostic break; 6151609Sbostic case 't': 6251609Sbostic tflag = 1; 6351609Sbostic break; 6451609Sbostic case '?': 658867Smckusick default: 6651609Sbostic usage(); 678867Smckusick } 6851609Sbostic argc -= optind; 6951609Sbostic argv += optind; 708867Smckusick 7151609Sbostic switch(argc) { 7251609Sbostic case 0: 7351609Sbostic get_file(indexfile); 7451609Sbostic show_index(); 7551609Sbostic break; 7651609Sbostic case 2: 7751609Sbostic get_file(indexfile); 7851609Sbostic get_cats(argv[0], argv[1]); 7951609Sbostic quiz(); 8051609Sbostic break; 8151609Sbostic default: 8251609Sbostic usage(); 838867Smckusick } 8451609Sbostic exit(0); 858867Smckusick } 868867Smckusick 8751609Sbostic void 8851609Sbostic get_file(file) 8951609Sbostic char *file; 908867Smckusick { 9151609Sbostic register FILE *fp; 9251609Sbostic register QE *qp; 9351609Sbostic size_t len; 9451609Sbostic char *lp; 958867Smckusick 9651609Sbostic if ((fp = fopen(file, "r")) == NULL) 9751609Sbostic err("%s: %s", file, strerror(errno)); 988867Smckusick 9951609Sbostic /* 10051609Sbostic * XXX 10151609Sbostic * Should really free up space from any earlier read list 10251609Sbostic * but there are no reverse pointers to do so with. 10351609Sbostic */ 10451609Sbostic qp = &qlist; 10551609Sbostic qsize = 0; 10651609Sbostic while ((lp = fgetline(fp, &len)) != NULL) { 10751609Sbostic if (qp->q_text && qp->q_text[strlen(qp->q_text) - 1] == '\\') 10851609Sbostic qp->q_text = appdstr(qp->q_text, lp); 10951609Sbostic else { 11051609Sbostic if ((qp->q_next = malloc(sizeof(QE))) == NULL) 11151609Sbostic err("%s", strerror(errno)); 11251609Sbostic qp = qp->q_next; 11351609Sbostic if ((qp->q_text = strdup(lp)) == NULL) 11451609Sbostic err("%s", strerror(errno)); 11551609Sbostic qp->q_asked = qp->q_answered = FALSE; 11651609Sbostic qp->q_next = NULL; 11751609Sbostic ++qsize; 1188867Smckusick } 1198867Smckusick } 12051609Sbostic (void)fclose(fp); 1218867Smckusick } 1228867Smckusick 12351609Sbostic void 12451609Sbostic show_index() 1258867Smckusick { 12651609Sbostic register QE *qp; 12751609Sbostic register char *p, *s; 12851609Sbostic FILE *pf; 1298867Smckusick 13051609Sbostic if ((pf = popen(_PATH_PAGER, "w")) == NULL) 13151609Sbostic err("%s: %s", _PATH_PAGER, strerror(errno)); 13251609Sbostic (void)fprintf(pf, "Subjects:\n\n"); 13351609Sbostic for (qp = qlist.q_next; qp; qp = qp->q_next) { 13451609Sbostic for (s = next_cat(qp->q_text); s; s = next_cat(s)) { 13551609Sbostic if (!rxp_compile(s)) 13651609Sbostic err("%s", rxperr); 13751609Sbostic if (p = rxp_expand()) 13851609Sbostic (void)fprintf(pf, "%s ", p); 1398867Smckusick } 14051609Sbostic (void)fprintf(pf, "\n"); 1418867Smckusick } 14251609Sbostic (void)fprintf(pf, "\n%s\n%s\n%s\n", 14351609Sbostic "For example, \"quiz victim killer\" prints a victim's name and you reply", 14451609Sbostic "with the killer, and \"quiz killer victim\" works the other way around.", 14551609Sbostic "Type an empty line to get the correct answer."); 14651609Sbostic (void)pclose(pf); 1478867Smckusick } 1488867Smckusick 14951609Sbostic void 15051609Sbostic get_cats(cat1, cat2) 15151609Sbostic char *cat1, *cat2; 1528867Smckusick { 15351609Sbostic register QE *qp; 15451609Sbostic int i; 15551609Sbostic char *s; 15651609Sbostic 15751609Sbostic downcase(cat1); 15851609Sbostic downcase(cat2); 15951609Sbostic for (qp = qlist.q_next; qp; qp = qp->q_next) { 16051609Sbostic s = next_cat(qp->q_text); 16151609Sbostic catone = cattwo = i = 0; 16251609Sbostic while (s) { 16351609Sbostic if (!rxp_compile(s)) 16451609Sbostic err("%s", rxperr); 16551609Sbostic i++; 16651609Sbostic if (rxp_match(cat1)) 16751609Sbostic catone = i; 16851609Sbostic if (rxp_match(cat2)) 16951609Sbostic cattwo = i; 17051609Sbostic s = next_cat(s); 1718867Smckusick } 17251609Sbostic if (catone && cattwo && catone != cattwo) { 17351609Sbostic if (!rxp_compile(qp->q_text)) 17451609Sbostic err("%s", rxperr); 17551609Sbostic get_file(rxp_expand()); 17651609Sbostic return; 17751609Sbostic } 1788867Smckusick } 17951609Sbostic err("invalid categories"); 1808867Smckusick } 1818867Smckusick 18251609Sbostic void 18351609Sbostic quiz() 1848867Smckusick { 18551609Sbostic register QE *qp; 18651609Sbostic register int i; 18751609Sbostic u_int guesses, rights, wrongs; 18851609Sbostic int next; 18951609Sbostic char *s, *t, question[LINE_SZ]; 19051609Sbostic char *answer; 1918867Smckusick 19251609Sbostic srandom(time(NULL)); 19351609Sbostic guesses = rights = wrongs = 0; 19451609Sbostic for (;;) { 19551609Sbostic if (qsize == 0) 1968867Smckusick break; 19751609Sbostic next = random() % qsize; 19851609Sbostic qp = qlist.q_next; 19951609Sbostic for (i = 0; i < next; i++) 20051609Sbostic qp = qp->q_next; 20151609Sbostic while (qp && qp->q_answered) 20251609Sbostic qp = qp->q_next; 20351609Sbostic if (!qp) { 20451609Sbostic qsize = next; 20551609Sbostic continue; 2068867Smckusick } 20751609Sbostic if (tflag && random() % 100 > 20) { 20851609Sbostic /* repeat questions in tutorial mode */ 20951609Sbostic while (qp && (!qp->q_asked || qp->q_answered)) 21051609Sbostic qp = qp->q_next; 21151609Sbostic if (!qp) 21251609Sbostic continue; 2138867Smckusick } 21451609Sbostic s = qp->q_text; 21551609Sbostic for (i = 0; i < catone - 1; i++) 21651609Sbostic s = next_cat(s); 21751609Sbostic if (!rxp_compile(s)) 21851609Sbostic err("%s", rxperr); 21951609Sbostic t = rxp_expand(); 22051609Sbostic if (!t || *t == '\0') { 22151609Sbostic qp->q_answered = TRUE; 2228867Smckusick continue; 2238867Smckusick } 22451609Sbostic (void)strcpy(question, t); 22551609Sbostic s = qp->q_text; 22651609Sbostic for (i = 0; i < cattwo - 1; i++) 22751609Sbostic s = next_cat(s); 22851609Sbostic if (!rxp_compile(s)) 22951609Sbostic err("%s", rxperr); 23051609Sbostic t = rxp_expand(); 23151609Sbostic if (!t || *t == '\0') { 23251609Sbostic qp->q_answered = TRUE; 23351609Sbostic continue; 23451609Sbostic } 23551609Sbostic qp->q_asked = TRUE; 23651609Sbostic (void)printf("%s?\n", question); 23751609Sbostic for (;; ++guesses) { 23851609Sbostic if ((answer = fgetline(stdin, NULL)) == NULL) { 23951609Sbostic score(rights, wrongs, guesses); 24051609Sbostic exit(0); 24151609Sbostic } 24251609Sbostic downcase(answer); 24351609Sbostic if (rxp_match(answer)) { 24451609Sbostic (void)printf("Right!\n"); 24551609Sbostic ++rights; 24651609Sbostic qp->q_answered = TRUE; 2478867Smckusick break; 2488867Smckusick } 24951609Sbostic if (*answer == '\0') { 25051609Sbostic (void)printf("%s\n", t); 25151609Sbostic ++wrongs; 25251609Sbostic if (!tflag) 25351609Sbostic qp->q_answered = TRUE; 2548867Smckusick break; 2558867Smckusick } 25651609Sbostic (void)printf("What?\n"); 2578867Smckusick } 2588867Smckusick } 25951609Sbostic score(rights, wrongs, guesses); 2608867Smckusick } 2618867Smckusick 26251609Sbostic char * 26351609Sbostic next_cat(s) 26451609Sbostic register char * s; 2658867Smckusick { 26651609Sbostic for (;;) 26751609Sbostic switch (*s++) { 26851609Sbostic case '\0': 26951609Sbostic return (NULL); 27051609Sbostic case '\\': 2718867Smckusick break; 27251609Sbostic case ':': 27351609Sbostic return (s); 2748867Smckusick } 27551609Sbostic /* NOTREACHED */ 2768867Smckusick } 2778867Smckusick 27851609Sbostic char * 27951609Sbostic appdstr(s, tp) 28051609Sbostic char *s; 28151609Sbostic register char *tp; 2828867Smckusick { 28351609Sbostic register char *mp, *sp; 28451609Sbostic register int ch; 28551609Sbostic char *m; 28651609Sbostic 28751609Sbostic if ((m = malloc(strlen(sp) + strlen(tp) + 1)) == NULL) 28851609Sbostic err("%s", strerror(errno)); 28951609Sbostic for (mp = m, sp = s; *mp++ = *sp++;); 29051609Sbostic 29151609Sbostic if (*(mp - 1) == '\\') 29251609Sbostic --mp; 29351609Sbostic while ((ch = *mp++ = *tp++) && ch != '\n'); 29451609Sbostic *mp = '\0'; 29551609Sbostic 29651609Sbostic free(s); 29751609Sbostic return (m); 2988867Smckusick } 2998867Smckusick 30046755Sbostic void 30151609Sbostic score(r, w, g) 30251609Sbostic u_int r, w, g; 3038867Smckusick { 30451609Sbostic (void)printf("Rights %d, wrongs %d,", r, w); 30551609Sbostic if (g) 30651609Sbostic (void)printf(" extra guesses %d,", g); 30751609Sbostic (void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0); 3088867Smckusick } 30951609Sbostic 31051609Sbostic void 31151609Sbostic downcase(p) 31251609Sbostic register char *p; 3138867Smckusick { 31451609Sbostic register int ch; 31551609Sbostic 31651609Sbostic for (; ch = *p; ++p) 31751609Sbostic if (isascii(ch) && isupper(ch)) 31851609Sbostic *p = tolower(ch); 3198867Smckusick } 3208867Smckusick 32151609Sbostic void 32251609Sbostic usage() 32351609Sbostic { 32451609Sbostic (void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n"); 32551609Sbostic exit(1); 3268867Smckusick } 3278867Smckusick 32851609Sbostic #if __STDC__ 32951609Sbostic #include <stdarg.h> 33051609Sbostic #else 33151609Sbostic #include <varargs.h> 33251609Sbostic #endif 33351609Sbostic 33451609Sbostic void 33551609Sbostic #if __STDC__ 33651609Sbostic err(const char *fmt, ...) 33751609Sbostic #else 33851609Sbostic err(fmt, va_alist) 33951609Sbostic char *fmt; 34051609Sbostic va_dcl 34151609Sbostic #endif 3428867Smckusick { 34351609Sbostic va_list ap; 34451609Sbostic #if __STDC__ 34551609Sbostic va_start(ap, fmt); 34651609Sbostic #else 34751609Sbostic va_start(ap); 34851609Sbostic #endif 34951609Sbostic (void)fprintf(stderr, "quiz: "); 35051609Sbostic (void)vfprintf(stderr, fmt, ap); 35151609Sbostic va_end(ap); 35251609Sbostic (void)fprintf(stderr, "\n"); 35351609Sbostic exit(1); 3548867Smckusick } 355