Home | History | Annotate | Line # | Download | only in quiz
quiz.c revision 1.1
      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.1  cgd static char sccsid[] = "@(#)quiz.c	5.1 (Berkeley) 11/10/91";
     45  1.1  cgd #endif /* not lint */
     46  1.1  cgd 
     47  1.1  cgd #include <sys/types.h>
     48  1.1  cgd #include <errno.h>
     49  1.1  cgd #include <time.h>
     50  1.1  cgd #include <stdio.h>
     51  1.1  cgd #include <stdlib.h>
     52  1.1  cgd #include <string.h>
     53  1.1  cgd #include <ctype.h>
     54  1.1  cgd #include "quiz.h"
     55  1.1  cgd #include "pathnames.h"
     56  1.1  cgd 
     57  1.1  cgd static QE qlist;
     58  1.1  cgd static int catone, cattwo, tflag;
     59  1.1  cgd static u_int qsize;
     60  1.1  cgd 
     61  1.1  cgd char	*appdstr __P((char *, char *));
     62  1.1  cgd void	 downcase __P((char *));
     63  1.1  cgd void	 err __P((const char *, ...));
     64  1.1  cgd void	 get_cats __P((char *, char *));
     65  1.1  cgd void	 get_file __P((char *));
     66  1.1  cgd char	*next_cat __P((char *));
     67  1.1  cgd void	 quiz __P((void));
     68  1.1  cgd void	 score __P((u_int, u_int, u_int));
     69  1.1  cgd void	 show_index __P((void));
     70  1.1  cgd void	 usage __P((void));
     71  1.1  cgd 
     72  1.1  cgd int
     73  1.1  cgd main(argc, argv)
     74  1.1  cgd 	int argc;
     75  1.1  cgd 	char *argv[];
     76  1.1  cgd {
     77  1.1  cgd 	register int ch;
     78  1.1  cgd 	char *indexfile;
     79  1.1  cgd 
     80  1.1  cgd 	indexfile = _PATH_QUIZIDX;
     81  1.1  cgd 	while ((ch = getopt(argc, argv, "i:t")) != EOF)
     82  1.1  cgd 		switch(ch) {
     83  1.1  cgd 		case 'i':
     84  1.1  cgd 			indexfile = optarg;
     85  1.1  cgd 			break;
     86  1.1  cgd 		case 't':
     87  1.1  cgd 			tflag = 1;
     88  1.1  cgd 			break;
     89  1.1  cgd 		case '?':
     90  1.1  cgd 		default:
     91  1.1  cgd 			usage();
     92  1.1  cgd 		}
     93  1.1  cgd 	argc -= optind;
     94  1.1  cgd 	argv += optind;
     95  1.1  cgd 
     96  1.1  cgd 	switch(argc) {
     97  1.1  cgd 	case 0:
     98  1.1  cgd 		get_file(indexfile);
     99  1.1  cgd 		show_index();
    100  1.1  cgd 		break;
    101  1.1  cgd 	case 2:
    102  1.1  cgd 		get_file(indexfile);
    103  1.1  cgd 		get_cats(argv[0], argv[1]);
    104  1.1  cgd 		quiz();
    105  1.1  cgd 		break;
    106  1.1  cgd 	default:
    107  1.1  cgd 		usage();
    108  1.1  cgd 	}
    109  1.1  cgd 	exit(0);
    110  1.1  cgd }
    111  1.1  cgd 
    112  1.1  cgd void
    113  1.1  cgd get_file(file)
    114  1.1  cgd 	char *file;
    115  1.1  cgd {
    116  1.1  cgd 	register FILE *fp;
    117  1.1  cgd 	register QE *qp;
    118  1.1  cgd 	size_t len;
    119  1.1  cgd 	char *lp;
    120  1.1  cgd 
    121  1.1  cgd 	if ((fp = fopen(file, "r")) == NULL)
    122  1.1  cgd 		err("%s: %s", file, strerror(errno));
    123  1.1  cgd 
    124  1.1  cgd 	/*
    125  1.1  cgd 	 * XXX
    126  1.1  cgd 	 * Should really free up space from any earlier read list
    127  1.1  cgd 	 * but there are no reverse pointers to do so with.
    128  1.1  cgd 	 */
    129  1.1  cgd 	qp = &qlist;
    130  1.1  cgd 	qsize = 0;
    131  1.1  cgd 	while ((lp = fgetline(fp, &len)) != NULL) {
    132  1.1  cgd 		if (qp->q_text && qp->q_text[strlen(qp->q_text) - 1] == '\\')
    133  1.1  cgd 			qp->q_text = appdstr(qp->q_text, lp);
    134  1.1  cgd 		else {
    135  1.1  cgd 			if ((qp->q_next = malloc(sizeof(QE))) == NULL)
    136  1.1  cgd 				err("%s", strerror(errno));
    137  1.1  cgd 			qp = qp->q_next;
    138  1.1  cgd 			if ((qp->q_text = strdup(lp)) == NULL)
    139  1.1  cgd 				err("%s", strerror(errno));
    140  1.1  cgd 			qp->q_asked = qp->q_answered = FALSE;
    141  1.1  cgd 			qp->q_next = NULL;
    142  1.1  cgd 			++qsize;
    143  1.1  cgd 		}
    144  1.1  cgd 	}
    145  1.1  cgd 	(void)fclose(fp);
    146  1.1  cgd }
    147  1.1  cgd 
    148  1.1  cgd void
    149  1.1  cgd show_index()
    150  1.1  cgd {
    151  1.1  cgd 	register QE *qp;
    152  1.1  cgd 	register char *p, *s;
    153  1.1  cgd 	FILE *pf;
    154  1.1  cgd 
    155  1.1  cgd 	if ((pf = popen(_PATH_PAGER, "w")) == NULL)
    156  1.1  cgd 		err("%s: %s", _PATH_PAGER, strerror(errno));
    157  1.1  cgd 	(void)fprintf(pf, "Subjects:\n\n");
    158  1.1  cgd 	for (qp = qlist.q_next; qp; qp = qp->q_next) {
    159  1.1  cgd 		for (s = next_cat(qp->q_text); s; s = next_cat(s)) {
    160  1.1  cgd 			if (!rxp_compile(s))
    161  1.1  cgd 				err("%s", rxperr);
    162  1.1  cgd 			if (p = rxp_expand())
    163  1.1  cgd 				(void)fprintf(pf, "%s ", p);
    164  1.1  cgd 		}
    165  1.1  cgd 		(void)fprintf(pf, "\n");
    166  1.1  cgd 	}
    167  1.1  cgd 	(void)fprintf(pf, "\n%s\n%s\n%s\n",
    168  1.1  cgd "For example, \"quiz victim killer\" prints a victim's name and you reply",
    169  1.1  cgd "with the killer, and \"quiz killer victim\" works the other way around.",
    170  1.1  cgd "Type an empty line to get the correct answer.");
    171  1.1  cgd 	(void)pclose(pf);
    172  1.1  cgd }
    173  1.1  cgd 
    174  1.1  cgd void
    175  1.1  cgd get_cats(cat1, cat2)
    176  1.1  cgd 	char *cat1, *cat2;
    177  1.1  cgd {
    178  1.1  cgd 	register QE *qp;
    179  1.1  cgd 	int i;
    180  1.1  cgd 	char *s;
    181  1.1  cgd 
    182  1.1  cgd 	downcase(cat1);
    183  1.1  cgd 	downcase(cat2);
    184  1.1  cgd 	for (qp = qlist.q_next; qp; qp = qp->q_next) {
    185  1.1  cgd 		s = next_cat(qp->q_text);
    186  1.1  cgd 		catone = cattwo = i = 0;
    187  1.1  cgd 		while (s) {
    188  1.1  cgd 			if (!rxp_compile(s))
    189  1.1  cgd 				err("%s", rxperr);
    190  1.1  cgd 			i++;
    191  1.1  cgd 			if (rxp_match(cat1))
    192  1.1  cgd 				catone = i;
    193  1.1  cgd 			if (rxp_match(cat2))
    194  1.1  cgd 				cattwo = i;
    195  1.1  cgd 			s = next_cat(s);
    196  1.1  cgd 		}
    197  1.1  cgd 		if (catone && cattwo && catone != cattwo) {
    198  1.1  cgd 			if (!rxp_compile(qp->q_text))
    199  1.1  cgd 				err("%s", rxperr);
    200  1.1  cgd 			get_file(rxp_expand());
    201  1.1  cgd 			return;
    202  1.1  cgd 		}
    203  1.1  cgd 	}
    204  1.1  cgd 	err("invalid categories");
    205  1.1  cgd }
    206  1.1  cgd 
    207  1.1  cgd void
    208  1.1  cgd quiz()
    209  1.1  cgd {
    210  1.1  cgd 	register QE *qp;
    211  1.1  cgd 	register int i;
    212  1.1  cgd 	u_int guesses, rights, wrongs;
    213  1.1  cgd 	int next;
    214  1.1  cgd 	char *s, *t, question[LINE_SZ];
    215  1.1  cgd 	char *answer;
    216  1.1  cgd 
    217  1.1  cgd 	srandom(time(NULL));
    218  1.1  cgd 	guesses = rights = wrongs = 0;
    219  1.1  cgd 	for (;;) {
    220  1.1  cgd 		if (qsize == 0)
    221  1.1  cgd 			break;
    222  1.1  cgd 		next = random() % qsize;
    223  1.1  cgd 		qp = qlist.q_next;
    224  1.1  cgd 		for (i = 0; i < next; i++)
    225  1.1  cgd 			qp = qp->q_next;
    226  1.1  cgd 		while (qp && qp->q_answered)
    227  1.1  cgd 			qp = qp->q_next;
    228  1.1  cgd 		if (!qp) {
    229  1.1  cgd 			qsize = next;
    230  1.1  cgd 			continue;
    231  1.1  cgd 		}
    232  1.1  cgd 		if (tflag && random() % 100 > 20) {
    233  1.1  cgd 			/* repeat questions in tutorial mode */
    234  1.1  cgd 			while (qp && (!qp->q_asked || qp->q_answered))
    235  1.1  cgd 				qp = qp->q_next;
    236  1.1  cgd 			if (!qp)
    237  1.1  cgd 				continue;
    238  1.1  cgd 		}
    239  1.1  cgd 		s = qp->q_text;
    240  1.1  cgd 		for (i = 0; i < catone - 1; i++)
    241  1.1  cgd 			s = next_cat(s);
    242  1.1  cgd 		if (!rxp_compile(s))
    243  1.1  cgd 			err("%s", rxperr);
    244  1.1  cgd 		t = rxp_expand();
    245  1.1  cgd 		if (!t || *t == '\0') {
    246  1.1  cgd 			qp->q_answered = TRUE;
    247  1.1  cgd 			continue;
    248  1.1  cgd 		}
    249  1.1  cgd 		(void)strcpy(question, t);
    250  1.1  cgd 		s = qp->q_text;
    251  1.1  cgd 		for (i = 0; i < cattwo - 1; i++)
    252  1.1  cgd 			s = next_cat(s);
    253  1.1  cgd 		if (!rxp_compile(s))
    254  1.1  cgd 			err("%s", rxperr);
    255  1.1  cgd 		t = rxp_expand();
    256  1.1  cgd 		if (!t || *t == '\0') {
    257  1.1  cgd 			qp->q_answered = TRUE;
    258  1.1  cgd 			continue;
    259  1.1  cgd 		}
    260  1.1  cgd 		qp->q_asked = TRUE;
    261  1.1  cgd 		(void)printf("%s?\n", question);
    262  1.1  cgd 		for (;; ++guesses) {
    263  1.1  cgd 			if ((answer = fgetline(stdin, NULL)) == NULL) {
    264  1.1  cgd 				score(rights, wrongs, guesses);
    265  1.1  cgd 				exit(0);
    266  1.1  cgd 			}
    267  1.1  cgd 			downcase(answer);
    268  1.1  cgd 			if (rxp_match(answer)) {
    269  1.1  cgd 				(void)printf("Right!\n");
    270  1.1  cgd 				++rights;
    271  1.1  cgd 				qp->q_answered = TRUE;
    272  1.1  cgd 				break;
    273  1.1  cgd 			}
    274  1.1  cgd 			if (*answer == '\0') {
    275  1.1  cgd 				(void)printf("%s\n", t);
    276  1.1  cgd 				++wrongs;
    277  1.1  cgd 				if (!tflag)
    278  1.1  cgd 					qp->q_answered = TRUE;
    279  1.1  cgd 				break;
    280  1.1  cgd 			}
    281  1.1  cgd 			(void)printf("What?\n");
    282  1.1  cgd 		}
    283  1.1  cgd 	}
    284  1.1  cgd 	score(rights, wrongs, guesses);
    285  1.1  cgd }
    286  1.1  cgd 
    287  1.1  cgd char *
    288  1.1  cgd next_cat(s)
    289  1.1  cgd 	register char *	s;
    290  1.1  cgd {
    291  1.1  cgd 	for (;;)
    292  1.1  cgd 		switch (*s++) {
    293  1.1  cgd 		case '\0':
    294  1.1  cgd 			return (NULL);
    295  1.1  cgd 		case '\\':
    296  1.1  cgd 			break;
    297  1.1  cgd 		case ':':
    298  1.1  cgd 			return (s);
    299  1.1  cgd 		}
    300  1.1  cgd 	/* NOTREACHED */
    301  1.1  cgd }
    302  1.1  cgd 
    303  1.1  cgd char *
    304  1.1  cgd appdstr(s, tp)
    305  1.1  cgd 	char *s;
    306  1.1  cgd 	register char *tp;
    307  1.1  cgd {
    308  1.1  cgd 	register char *mp, *sp;
    309  1.1  cgd 	register int ch;
    310  1.1  cgd 	char *m;
    311  1.1  cgd 
    312  1.1  cgd 	if ((m = malloc(strlen(sp) + strlen(tp) + 1)) == NULL)
    313  1.1  cgd 		err("%s", strerror(errno));
    314  1.1  cgd 	for (mp = m, sp = s; *mp++ = *sp++;);
    315  1.1  cgd 
    316  1.1  cgd 	if (*(mp - 1) == '\\')
    317  1.1  cgd 		--mp;
    318  1.1  cgd 	while ((ch = *mp++ = *tp++) && ch != '\n');
    319  1.1  cgd 	*mp = '\0';
    320  1.1  cgd 
    321  1.1  cgd 	free(s);
    322  1.1  cgd 	return (m);
    323  1.1  cgd }
    324  1.1  cgd 
    325  1.1  cgd void
    326  1.1  cgd score(r, w, g)
    327  1.1  cgd 	u_int r, w, g;
    328  1.1  cgd {
    329  1.1  cgd 	(void)printf("Rights %d, wrongs %d,", r, w);
    330  1.1  cgd 	if (g)
    331  1.1  cgd 		(void)printf(" extra guesses %d,", g);
    332  1.1  cgd 	(void)printf(" score %d%%\n", (r + w + g) ? r * 100 / (r + w + g) : 0);
    333  1.1  cgd }
    334  1.1  cgd 
    335  1.1  cgd void
    336  1.1  cgd downcase(p)
    337  1.1  cgd 	register char *p;
    338  1.1  cgd {
    339  1.1  cgd 	register int ch;
    340  1.1  cgd 
    341  1.1  cgd 	for (; ch = *p; ++p)
    342  1.1  cgd 		if (isascii(ch) && isupper(ch))
    343  1.1  cgd 			*p = tolower(ch);
    344  1.1  cgd }
    345  1.1  cgd 
    346  1.1  cgd void
    347  1.1  cgd usage()
    348  1.1  cgd {
    349  1.1  cgd 	(void)fprintf(stderr, "quiz [-t] [-i file] category1 category2\n");
    350  1.1  cgd 	exit(1);
    351  1.1  cgd }
    352  1.1  cgd 
    353  1.1  cgd #if __STDC__
    354  1.1  cgd #include <stdarg.h>
    355  1.1  cgd #else
    356  1.1  cgd #include <varargs.h>
    357  1.1  cgd #endif
    358  1.1  cgd 
    359  1.1  cgd void
    360  1.1  cgd #if __STDC__
    361  1.1  cgd err(const char *fmt, ...)
    362  1.1  cgd #else
    363  1.1  cgd err(fmt, va_alist)
    364  1.1  cgd 	char *fmt;
    365  1.1  cgd         va_dcl
    366  1.1  cgd #endif
    367  1.1  cgd {
    368  1.1  cgd 	va_list ap;
    369  1.1  cgd #if __STDC__
    370  1.1  cgd 	va_start(ap, fmt);
    371  1.1  cgd #else
    372  1.1  cgd 	va_start(ap);
    373  1.1  cgd #endif
    374  1.1  cgd 	(void)fprintf(stderr, "quiz: ");
    375  1.1  cgd 	(void)vfprintf(stderr, fmt, ap);
    376  1.1  cgd 	va_end(ap);
    377  1.1  cgd 	(void)fprintf(stderr, "\n");
    378  1.1  cgd 	exit(1);
    379  1.1  cgd }
    380