147862Sbostic /*- 260836Sbostic * Copyright (c) 1991, 1993 360836Sbostic * The Regents of the University of California. All rights reserved. 451609Sbostic * 551609Sbostic * This code is derived from software contributed to Berkeley by 652145Sbostic * Jim R. Oldroyd at The Instruction Set and Keith Gabryelski at 752145Sbostic * Commodore Business Machines. 851609Sbostic * 951609Sbostic * %sccs.include.redist.c% 1047862Sbostic */ 118867Smckusick 1247862Sbostic #ifndef lint 1360836Sbostic static char copyright[] = 1460836Sbostic "@(#) Copyright (c) 1991, 1993\n\ 1560836Sbostic The Regents of the University of California. All rights reserved.\n"; 1647862Sbostic #endif /* not lint */ 178867Smckusick 1851609Sbostic #ifndef lint 19*65360Sbostic static char sccsid[] = "@(#)quiz.c 8.2 (Berkeley) 01/03/94"; 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 3658443Sbostic char *appdstr __P((char *, char *, size_t)); 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; 106*65360Sbostic while ((lp = fgetln(fp, &len)) != NULL) { 10751609Sbostic if (qp->q_text && qp->q_text[strlen(qp->q_text) - 1] == '\\') 10858443Sbostic qp->q_text = appdstr(qp->q_text, lp, len); 10951609Sbostic else { 11051609Sbostic if ((qp->q_next = malloc(sizeof(QE))) == NULL) 11151609Sbostic err("%s", strerror(errno)); 11251609Sbostic qp = qp->q_next; 11358443Sbostic lp[len - 1] = '\0'; 11451609Sbostic if ((qp->q_text = strdup(lp)) == NULL) 11551609Sbostic err("%s", strerror(errno)); 11651609Sbostic qp->q_asked = qp->q_answered = FALSE; 11751609Sbostic qp->q_next = NULL; 11851609Sbostic ++qsize; 1198867Smckusick } 1208867Smckusick } 12151609Sbostic (void)fclose(fp); 1228867Smckusick } 1238867Smckusick 12451609Sbostic void 12551609Sbostic show_index() 1268867Smckusick { 12751609Sbostic register QE *qp; 12851609Sbostic register char *p, *s; 12951609Sbostic FILE *pf; 1308867Smckusick 13151609Sbostic if ((pf = popen(_PATH_PAGER, "w")) == NULL) 13251609Sbostic err("%s: %s", _PATH_PAGER, strerror(errno)); 13351609Sbostic (void)fprintf(pf, "Subjects:\n\n"); 13451609Sbostic for (qp = qlist.q_next; qp; qp = qp->q_next) { 13551609Sbostic for (s = next_cat(qp->q_text); s; s = next_cat(s)) { 13651609Sbostic if (!rxp_compile(s)) 13751609Sbostic err("%s", rxperr); 13851609Sbostic if (p = rxp_expand()) 13951609Sbostic (void)fprintf(pf, "%s ", p); 1408867Smckusick } 14151609Sbostic (void)fprintf(pf, "\n"); 1428867Smckusick } 14351609Sbostic (void)fprintf(pf, "\n%s\n%s\n%s\n", 14451609Sbostic "For example, \"quiz victim killer\" prints a victim's name and you reply", 14551609Sbostic "with the killer, and \"quiz killer victim\" works the other way around.", 14651609Sbostic "Type an empty line to get the correct answer."); 14751609Sbostic (void)pclose(pf); 1488867Smckusick } 1498867Smckusick 15051609Sbostic void 15151609Sbostic get_cats(cat1, cat2) 15251609Sbostic char *cat1, *cat2; 1538867Smckusick { 15451609Sbostic register QE *qp; 15551609Sbostic int i; 15651609Sbostic char *s; 15751609Sbostic 15851609Sbostic downcase(cat1); 15951609Sbostic downcase(cat2); 16051609Sbostic for (qp = qlist.q_next; qp; qp = qp->q_next) { 16151609Sbostic s = next_cat(qp->q_text); 16251609Sbostic catone = cattwo = i = 0; 16351609Sbostic while (s) { 16451609Sbostic if (!rxp_compile(s)) 16551609Sbostic err("%s", rxperr); 16651609Sbostic i++; 16751609Sbostic if (rxp_match(cat1)) 16851609Sbostic catone = i; 16951609Sbostic if (rxp_match(cat2)) 17051609Sbostic cattwo = i; 17151609Sbostic s = next_cat(s); 1728867Smckusick } 17351609Sbostic if (catone && cattwo && catone != cattwo) { 17451609Sbostic if (!rxp_compile(qp->q_text)) 17551609Sbostic err("%s", rxperr); 17651609Sbostic get_file(rxp_expand()); 17751609Sbostic return; 17851609Sbostic } 1798867Smckusick } 18051609Sbostic err("invalid categories"); 1818867Smckusick } 1828867Smckusick 18351609Sbostic void 18451609Sbostic quiz() 1858867Smckusick { 18651609Sbostic register QE *qp; 18751609Sbostic register int i; 18858443Sbostic size_t len; 18951609Sbostic u_int guesses, rights, wrongs; 19051609Sbostic int next; 19158443Sbostic char *answer, *s, *t, question[LINE_SZ]; 1928867Smckusick 19351609Sbostic srandom(time(NULL)); 19451609Sbostic guesses = rights = wrongs = 0; 19551609Sbostic for (;;) { 19651609Sbostic if (qsize == 0) 1978867Smckusick break; 19851609Sbostic next = random() % qsize; 19951609Sbostic qp = qlist.q_next; 20051609Sbostic for (i = 0; i < next; i++) 20151609Sbostic qp = qp->q_next; 20251609Sbostic while (qp && qp->q_answered) 20351609Sbostic qp = qp->q_next; 20451609Sbostic if (!qp) { 20551609Sbostic qsize = next; 20651609Sbostic continue; 2078867Smckusick } 20851609Sbostic if (tflag && random() % 100 > 20) { 20951609Sbostic /* repeat questions in tutorial mode */ 21051609Sbostic while (qp && (!qp->q_asked || qp->q_answered)) 21151609Sbostic qp = qp->q_next; 21251609Sbostic if (!qp) 21351609Sbostic continue; 2148867Smckusick } 21551609Sbostic s = qp->q_text; 21651609Sbostic for (i = 0; i < catone - 1; i++) 21751609Sbostic s = next_cat(s); 21851609Sbostic if (!rxp_compile(s)) 21951609Sbostic err("%s", rxperr); 22051609Sbostic t = rxp_expand(); 22151609Sbostic if (!t || *t == '\0') { 22251609Sbostic qp->q_answered = TRUE; 2238867Smckusick continue; 2248867Smckusick } 22551609Sbostic (void)strcpy(question, t); 22651609Sbostic s = qp->q_text; 22751609Sbostic for (i = 0; i < cattwo - 1; i++) 22851609Sbostic s = next_cat(s); 22951609Sbostic if (!rxp_compile(s)) 23051609Sbostic err("%s", rxperr); 23151609Sbostic t = rxp_expand(); 23251609Sbostic if (!t || *t == '\0') { 23351609Sbostic qp->q_answered = TRUE; 23451609Sbostic continue; 23551609Sbostic } 23651609Sbostic qp->q_asked = TRUE; 23751609Sbostic (void)printf("%s?\n", question); 23851609Sbostic for (;; ++guesses) { 239*65360Sbostic if ((answer = fgetln(stdin, &len)) == NULL) { 24051609Sbostic score(rights, wrongs, guesses); 24151609Sbostic exit(0); 24251609Sbostic } 24358443Sbostic answer[len - 1] = '\0'; 24451609Sbostic downcase(answer); 24551609Sbostic if (rxp_match(answer)) { 24651609Sbostic (void)printf("Right!\n"); 24751609Sbostic ++rights; 24851609Sbostic qp->q_answered = TRUE; 2498867Smckusick break; 2508867Smckusick } 25151609Sbostic if (*answer == '\0') { 25251609Sbostic (void)printf("%s\n", t); 25351609Sbostic ++wrongs; 25451609Sbostic if (!tflag) 25551609Sbostic qp->q_answered = TRUE; 2568867Smckusick break; 2578867Smckusick } 25851609Sbostic (void)printf("What?\n"); 2598867Smckusick } 2608867Smckusick } 26151609Sbostic score(rights, wrongs, guesses); 2628867Smckusick } 2638867Smckusick 26451609Sbostic char * 26551609Sbostic next_cat(s) 26651609Sbostic register char * s; 2678867Smckusick { 26851609Sbostic for (;;) 26951609Sbostic switch (*s++) { 27051609Sbostic case '\0': 27151609Sbostic return (NULL); 27251609Sbostic case '\\': 2738867Smckusick break; 27451609Sbostic case ':': 27551609Sbostic return (s); 2768867Smckusick } 27751609Sbostic /* NOTREACHED */ 2788867Smckusick } 2798867Smckusick 28051609Sbostic char * 28158443Sbostic appdstr(s, tp, len) 28251609Sbostic char *s; 28351609Sbostic register char *tp; 28458443Sbostic size_t len; 2858867Smckusick { 28651609Sbostic register char *mp, *sp; 28751609Sbostic register int ch; 28851609Sbostic char *m; 28951609Sbostic 29058443Sbostic if ((m = malloc(strlen(s) + len + 1)) == NULL) 29151609Sbostic err("%s", strerror(errno)); 29251609Sbostic for (mp = m, sp = s; *mp++ = *sp++;); 29351609Sbostic 29451609Sbostic if (*(mp - 1) == '\\') 29551609Sbostic --mp; 29651609Sbostic while ((ch = *mp++ = *tp++) && ch != '\n'); 29751609Sbostic *mp = '\0'; 29851609Sbostic 29951609Sbostic free(s); 30051609Sbostic return (m); 3018867Smckusick } 3028867Smckusick 30346755Sbostic void 30451609Sbostic score(r, w, g) 30551609Sbostic u_int r, w, g; 3068867Smckusick { 30751609Sbostic (void)printf("Rights %d, wrongs %d,", r, w); 30851609Sbostic if (g) 30951609Sbostic (void)printf(" extra guesses %d,", g); 31051609Sbostic (void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0); 3118867Smckusick } 31251609Sbostic 31351609Sbostic void 31451609Sbostic downcase(p) 31551609Sbostic register char *p; 3168867Smckusick { 31751609Sbostic register int ch; 31851609Sbostic 31951609Sbostic for (; ch = *p; ++p) 32051609Sbostic if (isascii(ch) && isupper(ch)) 32151609Sbostic *p = tolower(ch); 3228867Smckusick } 3238867Smckusick 32451609Sbostic void 32551609Sbostic usage() 32651609Sbostic { 32751609Sbostic (void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n"); 32851609Sbostic exit(1); 3298867Smckusick } 3308867Smckusick 33151609Sbostic #if __STDC__ 33251609Sbostic #include <stdarg.h> 33351609Sbostic #else 33451609Sbostic #include <varargs.h> 33551609Sbostic #endif 33651609Sbostic 33751609Sbostic void 33851609Sbostic #if __STDC__ 33951609Sbostic err(const char *fmt, ...) 34051609Sbostic #else 34151609Sbostic err(fmt, va_alist) 34251609Sbostic char *fmt; 34351609Sbostic va_dcl 34451609Sbostic #endif 3458867Smckusick { 34651609Sbostic va_list ap; 34751609Sbostic #if __STDC__ 34851609Sbostic va_start(ap, fmt); 34951609Sbostic #else 35051609Sbostic va_start(ap); 35151609Sbostic #endif 35251609Sbostic (void)fprintf(stderr, "quiz: "); 35351609Sbostic (void)vfprintf(stderr, fmt, ap); 35451609Sbostic va_end(ap); 35551609Sbostic (void)fprintf(stderr, "\n"); 35651609Sbostic exit(1); 3578867Smckusick } 358