(linenum→info "unix/slp.c:2238")

bsd-games/2.17/quiz/quiz.c

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