quiz.c revision 1.2 1 1.1 cgd /*-
2 1.1 cgd * Copyright (c) 1991 The Regents of the University of California.
3 1.1 cgd * All rights reserved.
4 1.1 cgd *
5 1.1 cgd * This code is derived from software contributed to Berkeley by
6 1.1 cgd * Jim R. Oldroyd at The Instruction Set.
7 1.1 cgd *
8 1.1 cgd * Redistribution and use in source and binary forms, with or without
9 1.1 cgd * modification, are permitted provided that the following conditions
10 1.1 cgd * are met:
11 1.1 cgd * 1. Redistributions of source code must retain the above copyright
12 1.1 cgd * notice, this list of conditions and the following disclaimer.
13 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 cgd * notice, this list of conditions and the following disclaimer in the
15 1.1 cgd * documentation and/or other materials provided with the distribution.
16 1.1 cgd * 3. All advertising materials mentioning features or use of this software
17 1.1 cgd * must display the following acknowledgement:
18 1.1 cgd * This product includes software developed by the University of
19 1.1 cgd * California, Berkeley and its contributors.
20 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
21 1.1 cgd * may be used to endorse or promote products derived from this software
22 1.1 cgd * without specific prior written permission.
23 1.1 cgd *
24 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 1.1 cgd * SUCH DAMAGE.
35 1.1 cgd */
36 1.1 cgd
37 1.1 cgd #ifndef lint
38 1.1 cgd char copyright[] =
39 1.1 cgd "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
40 1.1 cgd All rights reserved.\n";
41 1.1 cgd #endif /* not lint */
42 1.1 cgd
43 1.1 cgd #ifndef lint
44 1.2 mycroft /*static char sccsid[] = "from: @(#)quiz.c 5.1 (Berkeley) 11/10/91";*/
45 1.2 mycroft static char rcsid[] = "$Id: quiz.c,v 1.2 1993/08/01 18:52:59 mycroft Exp $";
46 1.1 cgd #endif /* not lint */
47 1.1 cgd
48 1.1 cgd #include <sys/types.h>
49 1.1 cgd #include <errno.h>
50 1.1 cgd #include <time.h>
51 1.1 cgd #include <stdio.h>
52 1.1 cgd #include <stdlib.h>
53 1.1 cgd #include <string.h>
54 1.1 cgd #include <ctype.h>
55 1.1 cgd #include "quiz.h"
56 1.1 cgd #include "pathnames.h"
57 1.1 cgd
58 1.1 cgd static QE qlist;
59 1.1 cgd static int catone, cattwo, tflag;
60 1.1 cgd static u_int qsize;
61 1.1 cgd
62 1.1 cgd char *appdstr __P((char *, char *));
63 1.1 cgd void downcase __P((char *));
64 1.1 cgd void err __P((const char *, ...));
65 1.1 cgd void get_cats __P((char *, char *));
66 1.1 cgd void get_file __P((char *));
67 1.1 cgd char *next_cat __P((char *));
68 1.1 cgd void quiz __P((void));
69 1.1 cgd void score __P((u_int, u_int, u_int));
70 1.1 cgd void show_index __P((void));
71 1.1 cgd void usage __P((void));
72 1.1 cgd
73 1.1 cgd int
74 1.1 cgd main(argc, argv)
75 1.1 cgd int argc;
76 1.1 cgd char *argv[];
77 1.1 cgd {
78 1.1 cgd register int ch;
79 1.1 cgd char *indexfile;
80 1.1 cgd
81 1.1 cgd indexfile = _PATH_QUIZIDX;
82 1.1 cgd while ((ch = getopt(argc, argv, "i:t")) != EOF)
83 1.1 cgd switch(ch) {
84 1.1 cgd case 'i':
85 1.1 cgd indexfile = optarg;
86 1.1 cgd break;
87 1.1 cgd case 't':
88 1.1 cgd tflag = 1;
89 1.1 cgd break;
90 1.1 cgd case '?':
91 1.1 cgd default:
92 1.1 cgd usage();
93 1.1 cgd }
94 1.1 cgd argc -= optind;
95 1.1 cgd argv += optind;
96 1.1 cgd
97 1.1 cgd switch(argc) {
98 1.1 cgd case 0:
99 1.1 cgd get_file(indexfile);
100 1.1 cgd show_index();
101 1.1 cgd break;
102 1.1 cgd case 2:
103 1.1 cgd get_file(indexfile);
104 1.1 cgd get_cats(argv[0], argv[1]);
105 1.1 cgd quiz();
106 1.1 cgd break;
107 1.1 cgd default:
108 1.1 cgd usage();
109 1.1 cgd }
110 1.1 cgd exit(0);
111 1.1 cgd }
112 1.1 cgd
113 1.1 cgd void
114 1.1 cgd get_file(file)
115 1.1 cgd char *file;
116 1.1 cgd {
117 1.1 cgd register FILE *fp;
118 1.1 cgd register QE *qp;
119 1.1 cgd size_t len;
120 1.1 cgd char *lp;
121 1.1 cgd
122 1.1 cgd if ((fp = fopen(file, "r")) == NULL)
123 1.1 cgd err("%s: %s", file, strerror(errno));
124 1.1 cgd
125 1.1 cgd /*
126 1.1 cgd * XXX
127 1.1 cgd * Should really free up space from any earlier read list
128 1.1 cgd * but there are no reverse pointers to do so with.
129 1.1 cgd */
130 1.1 cgd qp = &qlist;
131 1.1 cgd qsize = 0;
132 1.1 cgd while ((lp = fgetline(fp, &len)) != NULL) {
133 1.1 cgd if (qp->q_text && qp->q_text[strlen(qp->q_text) - 1] == '\\')
134 1.1 cgd qp->q_text = appdstr(qp->q_text, lp);
135 1.1 cgd else {
136 1.1 cgd if ((qp->q_next = malloc(sizeof(QE))) == NULL)
137 1.1 cgd err("%s", strerror(errno));
138 1.1 cgd qp = qp->q_next;
139 1.1 cgd if ((qp->q_text = strdup(lp)) == NULL)
140 1.1 cgd err("%s", strerror(errno));
141 1.1 cgd qp->q_asked = qp->q_answered = FALSE;
142 1.1 cgd qp->q_next = NULL;
143 1.1 cgd ++qsize;
144 1.1 cgd }
145 1.1 cgd }
146 1.1 cgd (void)fclose(fp);
147 1.1 cgd }
148 1.1 cgd
149 1.1 cgd void
150 1.1 cgd show_index()
151 1.1 cgd {
152 1.1 cgd register QE *qp;
153 1.1 cgd register char *p, *s;
154 1.1 cgd FILE *pf;
155 1.1 cgd
156 1.1 cgd if ((pf = popen(_PATH_PAGER, "w")) == NULL)
157 1.1 cgd err("%s: %s", _PATH_PAGER, strerror(errno));
158 1.1 cgd (void)fprintf(pf, "Subjects:\n\n");
159 1.1 cgd for (qp = qlist.q_next; qp; qp = qp->q_next) {
160 1.1 cgd for (s = next_cat(qp->q_text); s; s = next_cat(s)) {
161 1.1 cgd if (!rxp_compile(s))
162 1.1 cgd err("%s", rxperr);
163 1.1 cgd if (p = rxp_expand())
164 1.1 cgd (void)fprintf(pf, "%s ", p);
165 1.1 cgd }
166 1.1 cgd (void)fprintf(pf, "\n");
167 1.1 cgd }
168 1.1 cgd (void)fprintf(pf, "\n%s\n%s\n%s\n",
169 1.1 cgd "For example, \"quiz victim killer\" prints a victim's name and you reply",
170 1.1 cgd "with the killer, and \"quiz killer victim\" works the other way around.",
171 1.1 cgd "Type an empty line to get the correct answer.");
172 1.1 cgd (void)pclose(pf);
173 1.1 cgd }
174 1.1 cgd
175 1.1 cgd void
176 1.1 cgd get_cats(cat1, cat2)
177 1.1 cgd char *cat1, *cat2;
178 1.1 cgd {
179 1.1 cgd register QE *qp;
180 1.1 cgd int i;
181 1.1 cgd char *s;
182 1.1 cgd
183 1.1 cgd downcase(cat1);
184 1.1 cgd downcase(cat2);
185 1.1 cgd for (qp = qlist.q_next; qp; qp = qp->q_next) {
186 1.1 cgd s = next_cat(qp->q_text);
187 1.1 cgd catone = cattwo = i = 0;
188 1.1 cgd while (s) {
189 1.1 cgd if (!rxp_compile(s))
190 1.1 cgd err("%s", rxperr);
191 1.1 cgd i++;
192 1.1 cgd if (rxp_match(cat1))
193 1.1 cgd catone = i;
194 1.1 cgd if (rxp_match(cat2))
195 1.1 cgd cattwo = i;
196 1.1 cgd s = next_cat(s);
197 1.1 cgd }
198 1.1 cgd if (catone && cattwo && catone != cattwo) {
199 1.1 cgd if (!rxp_compile(qp->q_text))
200 1.1 cgd err("%s", rxperr);
201 1.1 cgd get_file(rxp_expand());
202 1.1 cgd return;
203 1.1 cgd }
204 1.1 cgd }
205 1.1 cgd err("invalid categories");
206 1.1 cgd }
207 1.1 cgd
208 1.1 cgd void
209 1.1 cgd quiz()
210 1.1 cgd {
211 1.1 cgd register QE *qp;
212 1.1 cgd register int i;
213 1.1 cgd u_int guesses, rights, wrongs;
214 1.1 cgd int next;
215 1.1 cgd char *s, *t, question[LINE_SZ];
216 1.1 cgd char *answer;
217 1.1 cgd
218 1.1 cgd srandom(time(NULL));
219 1.1 cgd guesses = rights = wrongs = 0;
220 1.1 cgd for (;;) {
221 1.1 cgd if (qsize == 0)
222 1.1 cgd break;
223 1.1 cgd next = random() % qsize;
224 1.1 cgd qp = qlist.q_next;
225 1.1 cgd for (i = 0; i < next; i++)
226 1.1 cgd qp = qp->q_next;
227 1.1 cgd while (qp && qp->q_answered)
228 1.1 cgd qp = qp->q_next;
229 1.1 cgd if (!qp) {
230 1.1 cgd qsize = next;
231 1.1 cgd continue;
232 1.1 cgd }
233 1.1 cgd if (tflag && random() % 100 > 20) {
234 1.1 cgd /* repeat questions in tutorial mode */
235 1.1 cgd while (qp && (!qp->q_asked || qp->q_answered))
236 1.1 cgd qp = qp->q_next;
237 1.1 cgd if (!qp)
238 1.1 cgd continue;
239 1.1 cgd }
240 1.1 cgd s = qp->q_text;
241 1.1 cgd for (i = 0; i < catone - 1; i++)
242 1.1 cgd s = next_cat(s);
243 1.1 cgd if (!rxp_compile(s))
244 1.1 cgd err("%s", rxperr);
245 1.1 cgd t = rxp_expand();
246 1.1 cgd if (!t || *t == '\0') {
247 1.1 cgd qp->q_answered = TRUE;
248 1.1 cgd continue;
249 1.1 cgd }
250 1.1 cgd (void)strcpy(question, t);
251 1.1 cgd s = qp->q_text;
252 1.1 cgd for (i = 0; i < cattwo - 1; i++)
253 1.1 cgd s = next_cat(s);
254 1.1 cgd if (!rxp_compile(s))
255 1.1 cgd err("%s", rxperr);
256 1.1 cgd t = rxp_expand();
257 1.1 cgd if (!t || *t == '\0') {
258 1.1 cgd qp->q_answered = TRUE;
259 1.1 cgd continue;
260 1.1 cgd }
261 1.1 cgd qp->q_asked = TRUE;
262 1.1 cgd (void)printf("%s?\n", question);
263 1.1 cgd for (;; ++guesses) {
264 1.1 cgd if ((answer = fgetline(stdin, NULL)) == NULL) {
265 1.1 cgd score(rights, wrongs, guesses);
266 1.1 cgd exit(0);
267 1.1 cgd }
268 1.1 cgd downcase(answer);
269 1.1 cgd if (rxp_match(answer)) {
270 1.1 cgd (void)printf("Right!\n");
271 1.1 cgd ++rights;
272 1.1 cgd qp->q_answered = TRUE;
273 1.1 cgd break;
274 1.1 cgd }
275 1.1 cgd if (*answer == '\0') {
276 1.1 cgd (void)printf("%s\n", t);
277 1.1 cgd ++wrongs;
278 1.1 cgd if (!tflag)
279 1.1 cgd qp->q_answered = TRUE;
280 1.1 cgd break;
281 1.1 cgd }
282 1.1 cgd (void)printf("What?\n");
283 1.1 cgd }
284 1.1 cgd }
285 1.1 cgd score(rights, wrongs, guesses);
286 1.1 cgd }
287 1.1 cgd
288 1.1 cgd char *
289 1.1 cgd next_cat(s)
290 1.1 cgd register char * s;
291 1.1 cgd {
292 1.1 cgd for (;;)
293 1.1 cgd switch (*s++) {
294 1.1 cgd case '\0':
295 1.1 cgd return (NULL);
296 1.1 cgd case '\\':
297 1.1 cgd break;
298 1.1 cgd case ':':
299 1.1 cgd return (s);
300 1.1 cgd }
301 1.1 cgd /* NOTREACHED */
302 1.1 cgd }
303 1.1 cgd
304 1.1 cgd char *
305 1.1 cgd appdstr(s, tp)
306 1.1 cgd char *s;
307 1.1 cgd register char *tp;
308 1.1 cgd {
309 1.1 cgd register char *mp, *sp;
310 1.1 cgd register int ch;
311 1.1 cgd char *m;
312 1.1 cgd
313 1.1 cgd if ((m = malloc(strlen(sp) + strlen(tp) + 1)) == NULL)
314 1.1 cgd err("%s", strerror(errno));
315 1.1 cgd for (mp = m, sp = s; *mp++ = *sp++;);
316 1.1 cgd
317 1.1 cgd if (*(mp - 1) == '\\')
318 1.1 cgd --mp;
319 1.1 cgd while ((ch = *mp++ = *tp++) && ch != '\n');
320 1.1 cgd *mp = '\0';
321 1.1 cgd
322 1.1 cgd free(s);
323 1.1 cgd return (m);
324 1.1 cgd }
325 1.1 cgd
326 1.1 cgd void
327 1.1 cgd score(r, w, g)
328 1.1 cgd u_int r, w, g;
329 1.1 cgd {
330 1.1 cgd (void)printf("Rights %d, wrongs %d,", r, w);
331 1.1 cgd if (g)
332 1.1 cgd (void)printf(" extra guesses %d,", g);
333 1.1 cgd (void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0);
334 1.1 cgd }
335 1.1 cgd
336 1.1 cgd void
337 1.1 cgd downcase(p)
338 1.1 cgd register char *p;
339 1.1 cgd {
340 1.1 cgd register int ch;
341 1.1 cgd
342 1.1 cgd for (; ch = *p; ++p)
343 1.1 cgd if (isascii(ch) && isupper(ch))
344 1.1 cgd *p = tolower(ch);
345 1.1 cgd }
346 1.1 cgd
347 1.1 cgd void
348 1.1 cgd usage()
349 1.1 cgd {
350 1.1 cgd (void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n");
351 1.1 cgd exit(1);
352 1.1 cgd }
353 1.1 cgd
354 1.1 cgd #if __STDC__
355 1.1 cgd #include <stdarg.h>
356 1.1 cgd #else
357 1.1 cgd #include <varargs.h>
358 1.1 cgd #endif
359 1.1 cgd
360 1.1 cgd void
361 1.1 cgd #if __STDC__
362 1.1 cgd err(const char *fmt, ...)
363 1.1 cgd #else
364 1.1 cgd err(fmt, va_alist)
365 1.1 cgd char *fmt;
366 1.1 cgd va_dcl
367 1.1 cgd #endif
368 1.1 cgd {
369 1.1 cgd va_list ap;
370 1.1 cgd #if __STDC__
371 1.1 cgd va_start(ap, fmt);
372 1.1 cgd #else
373 1.1 cgd va_start(ap);
374 1.1 cgd #endif
375 1.1 cgd (void)fprintf(stderr, "quiz: ");
376 1.1 cgd (void)vfprintf(stderr, fmt, ap);
377 1.1 cgd va_end(ap);
378 1.1 cgd (void)fprintf(stderr, "\n");
379 1.1 cgd exit(1);
380 1.1 cgd }
381