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