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