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