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