Commit | Line | Data |
---|---|---|
dc7f5a19 | 1 | /*- |
a0be8f19 KB |
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 | |
ec70c3a9 KB |
6 | * Jim R. Oldroyd at The Instruction Set and Keith Gabryelski at |
7 | * Commodore Business Machines. | |
a0be8f19 KB |
8 | * |
9 | * %sccs.include.redist.c% | |
dc7f5a19 | 10 | */ |
be9db06c | 11 | |
dc7f5a19 | 12 | #ifndef lint |
a0be8f19 KB |
13 | char copyright[] = |
14 | "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ | |
15 | All rights reserved.\n"; | |
dc7f5a19 | 16 | #endif /* not lint */ |
be9db06c | 17 | |
a0be8f19 | 18 | #ifndef lint |
ec70c3a9 | 19 | static char sccsid[] = "@(#)quiz.c 5.2 (Berkeley) %G%"; |
a0be8f19 KB |
20 | #endif /* not lint */ |
21 | ||
22 | #include <sys/types.h> | |
23 | #include <errno.h> | |
24 | #include <time.h> | |
be9db06c | 25 | #include <stdio.h> |
a0be8f19 KB |
26 | #include <stdlib.h> |
27 | #include <string.h> | |
28 | #include <ctype.h> | |
29 | #include "quiz.h" | |
96deaadb KB |
30 | #include "pathnames.h" |
31 | ||
a0be8f19 KB |
32 | static QE qlist; |
33 | static int catone, cattwo, tflag; | |
34 | static u_int qsize; | |
be9db06c | 35 | |
a0be8f19 KB |
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)); | |
be9db06c | 46 | |
a0be8f19 KB |
47 | int |
48 | main(argc, argv) | |
49 | int argc; | |
50 | char *argv[]; | |
be9db06c | 51 | { |
638cfd36 | 52 | register int ch; |
a0be8f19 | 53 | char *indexfile; |
be9db06c | 54 | |
a0be8f19 KB |
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 '?': | |
be9db06c | 65 | default: |
a0be8f19 | 66 | usage(); |
be9db06c | 67 | } |
a0be8f19 KB |
68 | argc -= optind; |
69 | argv += optind; | |
be9db06c | 70 | |
a0be8f19 KB |
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(); | |
be9db06c | 83 | } |
a0be8f19 | 84 | exit(0); |
be9db06c KM |
85 | } |
86 | ||
a0be8f19 KB |
87 | void |
88 | get_file(file) | |
89 | char *file; | |
be9db06c | 90 | { |
a0be8f19 KB |
91 | register FILE *fp; |
92 | register QE *qp; | |
93 | size_t len; | |
94 | char *lp; | |
be9db06c | 95 | |
a0be8f19 KB |
96 | if ((fp = fopen(file, "r")) == NULL) |
97 | err("%s: %s", file, strerror(errno)); | |
be9db06c | 98 | |
a0be8f19 KB |
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; | |
be9db06c KM |
118 | } |
119 | } | |
a0be8f19 | 120 | (void)fclose(fp); |
be9db06c KM |
121 | } |
122 | ||
a0be8f19 KB |
123 | void |
124 | show_index() | |
be9db06c | 125 | { |
a0be8f19 KB |
126 | register QE *qp; |
127 | register char *p, *s; | |
128 | FILE *pf; | |
be9db06c | 129 | |
a0be8f19 KB |
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); | |
be9db06c | 139 | } |
a0be8f19 | 140 | (void)fprintf(pf, "\n"); |
be9db06c | 141 | } |
a0be8f19 KB |
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); | |
be9db06c KM |
147 | } |
148 | ||
a0be8f19 KB |
149 | void |
150 | get_cats(cat1, cat2) | |
151 | char *cat1, *cat2; | |
be9db06c | 152 | { |
a0be8f19 KB |
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; | |
be9db06c | 177 | } |
be9db06c | 178 | } |
a0be8f19 | 179 | err("invalid categories"); |
be9db06c KM |
180 | } |
181 | ||
a0be8f19 KB |
182 | void |
183 | quiz() | |
be9db06c | 184 | { |
a0be8f19 KB |
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; | |
be9db06c | 191 | |
a0be8f19 KB |
192 | srandom(time(NULL)); |
193 | guesses = rights = wrongs = 0; | |
194 | for (;;) { | |
195 | if (qsize == 0) | |
be9db06c | 196 | break; |
a0be8f19 KB |
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; | |
be9db06c | 206 | } |
a0be8f19 KB |
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; | |
be9db06c | 213 | } |
a0be8f19 KB |
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; | |
be9db06c KM |
233 | continue; |
234 | } | |
a0be8f19 KB |
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; | |
be9db06c KM |
247 | break; |
248 | } | |
a0be8f19 KB |
249 | if (*answer == '\0') { |
250 | (void)printf("%s\n", t); | |
251 | ++wrongs; | |
252 | if (!tflag) | |
253 | qp->q_answered = TRUE; | |
be9db06c KM |
254 | break; |
255 | } | |
a0be8f19 | 256 | (void)printf("What?\n"); |
be9db06c | 257 | } |
be9db06c | 258 | } |
a0be8f19 | 259 | score(rights, wrongs, guesses); |
be9db06c KM |
260 | } |
261 | ||
a0be8f19 KB |
262 | char * |
263 | next_cat(s) | |
264 | register char * s; | |
be9db06c | 265 | { |
a0be8f19 KB |
266 | for (;;) |
267 | switch (*s++) { | |
268 | case '\0': | |
269 | return (NULL); | |
270 | case '\\': | |
be9db06c | 271 | break; |
a0be8f19 KB |
272 | case ':': |
273 | return (s); | |
be9db06c | 274 | } |
a0be8f19 | 275 | /* NOTREACHED */ |
be9db06c KM |
276 | } |
277 | ||
a0be8f19 KB |
278 | char * |
279 | appdstr(s, tp) | |
280 | char *s; | |
281 | register char *tp; | |
be9db06c | 282 | { |
a0be8f19 KB |
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); | |
be9db06c KM |
298 | } |
299 | ||
786b3585 | 300 | void |
a0be8f19 KB |
301 | score(r, w, g) |
302 | u_int r, w, g; | |
be9db06c | 303 | { |
a0be8f19 KB |
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); | |
be9db06c | 308 | } |
a0be8f19 KB |
309 | |
310 | void | |
311 | downcase(p) | |
312 | register char *p; | |
be9db06c | 313 | { |
a0be8f19 KB |
314 | register int ch; |
315 | ||
316 | for (; ch = *p; ++p) | |
317 | if (isascii(ch) && isupper(ch)) | |
318 | *p = tolower(ch); | |
be9db06c KM |
319 | } |
320 | ||
a0be8f19 KB |
321 | void |
322 | usage() | |
323 | { | |
324 | (void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n"); | |
325 | exit(1); | |
be9db06c KM |
326 | } |
327 | ||
a0be8f19 KB |
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 | |
be9db06c | 342 | { |
a0be8f19 KB |
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); | |
be9db06c | 354 | } |