[comp.sources.amiga] v91i085: Quiz 1.0 - question and answer program, Part02/02

amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (04/10/91)

Submitted-by: creubank@is.crl.sony.co.jp
Posting-number: Volume 91, Issue 085
Archive-name: applications/quiz-1.0/part02

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  parse.c quiz.c screen.c
# Wrapped by tadguy@ab20 on Wed Apr 10 10:57:36 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'parse.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'parse.c'\"
else
echo shar: Extracting \"'parse.c'\" \(11191 characters\)
sed "s/^X//" >'parse.c' <<'END_OF_FILE'
X/***
X ***  parse.c
X ***
X ***  Curtis Eubanks
X ***  3 November 1989
X ***
X ***/
X
X#include <stdio.h>
X#include <string.h>
X
X#include <exec/types.h> /* only for def'n of 'node' */
X#include <exec/lists.h> /* needed in qstructs.h     */
X
X/*#ifndef EXEC_MEMORY_H
X#include <exec/memory.h>
X#endif
X*/
X#define GLOBALVAR extern
X#include "qstructs.h"
Xextern char *mymalloc();
X
Xvoid slistAdd();
Xstruct slist *Makeslist();
Xint slistSize();
Xvoid remwh();
Xchar *findfirstbrace();
Xchar *findlevel();
Xvoid deletebrackets();
Xchar *snarfbrackets();
Xvoid printslist();
Xstruct slist *FindOrs();
Xstruct slist *findors();
Xint checkbrackets();
Xstruct slist *joinslists();
Xchar *findmatchbrace();
Xstruct slist *expandslist();
Xstruct slist *expand();
X
X/* Add a single string to slist sl.  Does not copy */
Xvoid slistAdd(sl,s) struct slist *sl; char *s;
X{
X  while (sl->next) sl = sl->next;
X  sl->next = (struct slist *)mymalloc(sizeof(struct slist));
X  (sl->next)->s = s;
X  (sl->next)->next = NULL;
X} /* slistAdd */
X
X/* returns the number of elems in sl */
Xint slistSize(sl) struct slist *sl;
X{
X int rtn=0;
X while (sl) rtn++, sl=sl->next;
X return(rtn);
X} /* slistSize */
X
X/* allocate and return a new slist structure and fill it with 's' */
Xstruct slist *Makeslist(s) char *s;
X{
X  struct slist *rtn;
X  rtn = (struct slist *)mymalloc(sizeof(struct slist));
X  rtn->s = s;
X  rtn->next = NULL;
X  return(rtn);
X} /* Makeslist */
X
X/* whitespace macro */
X#define iswhite(x) ((x == ' ')||(x == '\t'))
X
X/* modifies s by removing leading & trailing whitespace and replacing
X   any other whitespace with a single space */
Xvoid remwh(s) char *s;
X{
X  char *r;
X  r = s; /* start scanning at the beginning */
X  if (!(*s)) return; /* a null string could mess us up */
X  while (1) {
X    while ((*r) && iswhite(*r)) r++;
X    if (!(*r)) {
X      /* we may have to delete a single ' ' at the end */
X      /* since we already check for a null string passed to remwh, */
X      /* we know that *(s-1) is a valid address */
X      if (iswhite(*(s-1))) *(s-1) = '\0';
X      else *s = '\0';
X      return;
X    } /* if !*r */
X    /* now copy till next white */
X    while ((*r) && (!(iswhite(*r)))) *s++ = *r++;
X    if (*r) *s++ = ' '; /* replace white with a space */
X    } /* while (1) */
X} /* remwh */
X
X/* returns the address of the first opening brace '{' in s.
X   If none found, returns NULL */
Xchar *findfirstbrace(s)
X	char *s;
X{
X  while (*s) {
X    if (*s == '{') return(s);
X    s++;
X   } /* while */
X   return(NULL);
X} /* findfirstbrace */
X
X/* returns the address of first opening brace in s that is nested level
X   levels deep.  Returns NULL if not found */
Xchar *findlevel(s, level)
X	char *s;
X	int level;
X{
X  int nest = 0;
X
X  if (level == 0) return(s);
X  while (*s) {
X    if (*s == '{') {
X      nest++;
X      if (nest==level) return(s+1);
X    } /* if */
X    else if (*s == '}') nest--;
X    s++;
X  } /* while */
Xreturn(NULL);
X} /* findlevel */
X
X/* Modifies s by deleting the first set of matching brackets in s.
X   Requires s has at least one set of balanced brackets.
X   deletebrackets("this {is {an} example} here") --> modifies s to -->
X   "this is {an} example here" */
Xvoid deletebrackets(s) char *s;
X{
X  int nest = 1;
X  char *t;
X
X  while (*s != '{') s++;
X  t = s+1;
X  while (*t) {
X    if (*t == '{') nest++;
X    else if (*t == '}') {
X      nest--;
X      if (!nest) { t++; break; } /* found end brace */
X      } /* else if */
X  *s++ = *t++;
X  } /* while *t */
X  while (*t) *s++ = *t++; /* copy the rest of the string */
X  *s = '\0';
X} /* deletebrackets */
X
X/* snarfbrackets is the same as deletebrackets except that it actually
X   deletes everything within and including the brackets.  Returns a NEW
X   string.  Doesn't modify s.  Requires the brackets exist.
X   snarfbrackets("this {is {an} example} here") = "this  here" */
Xchar *snarfbrackets(s)
X	char *s;
X{
X  char *first, *last, *rtn;
X
X  rtn = mymalloc(strlen(s)+1);
X  (void)strcpy(rtn, s);
X  first = findfirstbrace(rtn);
X  if (!first)
X    FatalError("Aiigghh!  Couldn't find first brace in %s\n",rtn);
X  last = findmatchbrace(first)+1;
X  if (!last)
X    FatalError("Aiigghh!  Couldn't find matching brace in %s\n",first);
X  while (*last) *first++ = *last++;
X  *first = '\0';
X  return(rtn);
X}
X
X/* Prints out all the strings in an slist structure, each on a different
X   line.  Used only for debugging */
Xvoid printslist(sl) struct slist *sl;
X{
X  printf("Slist is:\n");
X  while (sl) { printf("    \"%s\"\n",sl->s); sl = sl->next; }
X  printf("\n");
X}
X
X/* FindOrs returns a list of outermost level strings of s that are
X   separated by '|'.  Requires s have balanced brackets.  If only one
X   string is found, NULL is returned.
X   FindOrs("one|{two|zwei|ni}|mikka") =
X   { "one", "{two|zwei|ni}", "mikka"} */
Xstruct slist *FindOrs(s) char *s;
X{
X  struct slist *rtn = NULL;
X  int tmpsize, numfound = 0, nest = 0;
X  char *lastword, *tmpstr;
X
X  if (*s == '\0') return(rtn);
X  lastword = s;
X  while (*s) {
X    if (*s == '{') nest++;
X    else if (*s == '}') nest--;
X    if ((*s == '|')&&(!nest)) {
X      numfound++;
X      tmpsize = s-lastword+1;
X      tmpstr = mymalloc(tmpsize);
X      memcpy(tmpstr, lastword, tmpsize-1);
X      tmpstr[tmpsize-1] = '\0';
X      if (!rtn) rtn = Makeslist(tmpstr);
X      else slistAdd(rtn, tmpstr);
X      lastword = s+1;
X      } /* if */
X    s++;
X    } /* while */
X  if (numfound == 0) return(NULL);
X  if (*lastword) {
X    numfound++;
X    tmpsize = s-lastword+1;
X    tmpstr = mymalloc(tmpsize);
X    memcpy(tmpstr, lastword, tmpsize-1);
X    tmpstr[tmpsize-1] = '\0';
X    if (!rtn) rtn = Makeslist(tmpstr);
X    else slistAdd(rtn, tmpstr);
X    } /* if */
X   return(rtn);
X} /* FindOrs */
X
X/* findors (probably not the best name) is similar to FindOrs, except
X   that it returns the '|'-separated strings between the first set of
X   balanced brackets.  Requires at least one set of balanced braces.
X   findors("not in braces {a|b|{c|d}} not included") =
X    { "a", "b", "{c|d}" } 
X   As with FindOrs, findors returns NULL if only a single string is
X   found or if s is an empty string or if no braces are found.  This
X   was probably a bad idea. */
Xstruct slist *findors(s) char *s;
X{
X  struct slist *rtn = NULL;
X  int tmpsize, numfound = 0, nest = 1;
X  char *lastword, *tmpstr;
X
X  if (*s == '\0') return(NULL);
X
X  lastword = findfirstbrace(s);
X  if (!lastword) return(NULL); /* look ma, no braces */
X  lastword++; /* point to actual string, not brace */
X  s = lastword;
X  while (*s) {
X    if (*s=='{') nest++;
X    else if (*s=='}') {
X      nest--;
X      if (!nest) {
X        numfound++;
X        tmpsize = s-lastword+1;
X        tmpstr = mymalloc(tmpsize);
X        memcpy(tmpstr, lastword, tmpsize-1);
X        tmpstr[tmpsize-1] = '\0';
X        if (!rtn) rtn = Makeslist(tmpstr);
X        else slistAdd(rtn, tmpstr);
X        break;
X	} /* if !nest */
X    } /* if close bracket */
X    else if ((*s == '|') && (nest==1)) {
X      numfound++;
X      tmpsize = s-lastword+1;
X      tmpstr = mymalloc(tmpsize);
X      memcpy(tmpstr, lastword, tmpsize-1);
X      tmpstr[tmpsize-1] = '\0';
X      if (!rtn) rtn = Makeslist(tmpstr);
X      else slistAdd(rtn, tmpstr);
X      lastword = s+1;
X      } /* if */
X    s++;
X    } /* while */
X    if (numfound==1) return(NULL);
X    return(rtn);
X } /* findors */
X
X/* Returns deepest level of bracket nesting in a regular expression
X   string.  Returns -1 if there is an error (unbalanced or unmatched
X   brackets. */
Xint checkbrackets(s) char *s;
X{
X  int nest = 0, maxlevel = 0;
X
X  while (*s) {
X    if (*s == '{') {
X      nest++;
X      if (nest > maxlevel) maxlevel = nest;
X      } /* if open brace */
X    else if (*s == '}') {
X      nest--;
X      if (nest < 0) return(-1);
X      } /* if close brace */
X    s++;
X    } /* while */
X    if (nest != 0) return(-1);
X    return(maxlevel);
X} /* checkbrackets */
X
X/* Joins two slists by adding sl2 to the end of sl1.  Does not copy
X   sl2.  Modifies sl1. There's really no reason to return sl1... */
Xstruct slist *joinslists(sl1, sl2)
X   struct slist *sl1, *sl2;
X{
X   struct slist *tmpslist; /* so we can return sl1 */
X
X   if (sl2) {
X     tmpslist = sl1;
X     while (tmpslist->next) tmpslist = tmpslist->next;
X     tmpslist->next = sl2;
X     } /* if sl2 */
X   return(sl1);
X} /* joinslists */
X
X/* find and return pointer to a matching brace; requires such a
X   brace exists!  Returns NULL if not found. */
Xchar *findmatchbrace(s)
X	char *s;
X{
X  int nest = 0;
X
X  s = findfirstbrace(s);
X  while (*s) {
X    if (*s == '{') nest++;
X    else if (*s == '}') {
X      nest--;
X      if (nest==0) return(s);
X      } /* if close */
X    s++;
X    } /* while */
X   return(NULL);
X} /* findmatchbrace */
X
X/* expandslist expects a list of strings.  Each string is considered to
X   be a simple regular expression of using '{', '}', and '|'.  The
X   strings are expanded to all possibilities indicated by the regexp.
X   "a|b|c" --> "a", "b", "c"
X   "{x|y|z}-axis" --> "x-axis", "y-axis", "z-axis"
X   "this {option} is fun" --> "this option is fun", "this  is fun" */
Xstruct slist *expandslist(sl)
X   struct slist *sl;
X{
X  struct slist *tmpslist, *rtn = NULL, workslist, *braceslist;
X  char *bptr, *eptr, *newstr;
X  int beforelength, afterlength;
X
X  workslist.next = NULL; /* used with a single string for efficiency */
X  while (sl) { /* repeat for each string in sl */
X    tmpslist = FindOrs(sl->s); /* get top level disjunctions */
X    if (!tmpslist) {tmpslist=&workslist;workslist.s=sl->s;}
X    while (tmpslist) {
X      if (bptr = findfirstbrace(tmpslist->s)) { /* there is a brace */
X        braceslist = findors(bptr); /* or tmpslist->s */
X        if (!braceslist) { /* "{degenerate case}" */
X          /* we want "{x}" to map to "x" and "" */
X          slistAdd(sl, snarfbrackets(tmpslist->s)); /* "" */
X          deletebrackets(tmpslist->s);
X          slistAdd(sl, tmpslist->s);
X          tmpslist = tmpslist->next;
X          continue;
X          } /* if "{x}" */
X        eptr = findmatchbrace(bptr) + 1;
X        beforelength = bptr - tmpslist->s;
X        afterlength = tmpslist->s + strlen(tmpslist->s) - eptr;
X        while (braceslist) {
X          newstr = mymalloc(beforelength+strlen(braceslist->s)+afterlength+1);
X          memcpy(newstr, tmpslist->s, beforelength);
X          strcat(newstr, braceslist->s);
X          strcat(newstr, eptr);
X          slistAdd(sl, newstr);
X          braceslist = braceslist->next;
X          } /* while */
X        } /* if there is a brace */
X      else { /* no brace, just add the string to return list */
X        if (!rtn) rtn = Makeslist(tmpslist->s);
X        else slistAdd(rtn, tmpslist->s);
X        } /* else */
X      tmpslist = tmpslist->next; /* iterate */
X      } /* while tmpslist */
X    sl = sl->next; /* iterate */
X    } /* while sl */
X  return(rtn);
X} /* expandslist */
X
X/* expand expands a single string by calling expandslist. */
X/* returns the expanded slist, or NULL if error */
Xstruct slist *expand(s) char *s;
X{
X  int level;
X  struct slist *rtn, *tmpslist;
X
X  level = checkbrackets(s);
X  if (level<0) {
X    fprintf(stderr, "Unbalanced braces in \"%s\"\n", s);
X    return(NULL);
X    } /* if */
X  rtn = expandslist(Makeslist(s));
X  tmpslist = rtn;
X  while (tmpslist) {
X    remwh(tmpslist->s);
X    tmpslist = tmpslist->next;
X    } /* while */
X  return(rtn);
X} /* expand */
END_OF_FILE
if test 11191 -ne `wc -c <'parse.c'`; then
    echo shar: \"'parse.c'\" unpacked with wrong size!
fi
# end of 'parse.c'
fi
if test -f 'quiz.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'quiz.c'\"
else
echo shar: Extracting \"'quiz.c'\" \(26723 characters\)
sed "s/^X//" >'quiz.c' <<'END_OF_FILE'
X/***
X ***  quiz.c
X ***  a question-answer game similar to /usr/games/quiz.
X ***
X ***  Curtis Eubanks
X ***  19 October 1989  -- CLI only
X ***  18 November 1989 -- graphics interface
X ***
X ***/
X
X/* screen.c funcs */
Xextern void InitGraphics(), CloseGraphics(), redraw(),
X  UpdateMenuGraphState();
X
X/* graph.c funcs */
Xextern void cleargraph(),drawgraph(), UpdateCurrentQ();
X
X#include <exec/types.h>
X#include <exec/lists.h>
X#include <stdio.h>
X#include <string.h>
X#include <libraries/dos.h>
X#include <libraries/dosextens.h>
X#include <exec/memory.h>
X#include <workbench/startup.h>
X#include <stdlib.h> /* for srand & rand */
X#include <intuition/intuition.h>
X
Xextern struct FileHandle *Open();
Xextern long time();
X/*#include <time.h>    for time()       */
X
X
Xextern int bhandlemsg();
Xextern ULONG ehandlemsg();
Xextern struct NewWindow backnw;
Xextern struct NewScreen ns;
Xextern struct Window *backwindow;
Xextern struct IntuiMessage *mes, *GetMsg();
Xextern char *qtext, *atext, *sbuff, *ubuff, *ctext;
Xextern struct StringInfo sStringInfo;
Xextern void DrawQText();
Xextern struct Gadget sgadget;
X#define BEGINMENUNUM (SHIFTITEM(2)|SHIFTSUB(NOSUB))
X
X/* type definitions */
X#define GLOBALVAR
X#include "qstructs.h"
X#include "parse.c.h"
X#include "graph.h"
X
Xextern char *mymalloc(); /* in mymalloc.c */
X
X/* in graph.c */
Xextern int readscore();
Xextern int *AddTrial();
Xextern void SetWorstQuestion();
X
X/***** function defs ******/
Xvoid usage();
Xvoid parseargs();
Xstruct ResListItem *MakeNewItem();
Xvoid FreeResource();
Xvoid FreeAll();
Xvoid CleanUp();
Xchar *Allocate();
Xvoid FatalError();
Xvoid NonFatalError();
Xint readfile();
Xint countlines();
Xint isin();
Xchar *separate();
Xvoid ask();
Xvoid getinput();
Xvoid correct();
Xvoid wrong();
Xchar **extractlines();
Xvoid ilistAdd();
Xilist *Makeilist();
Xint ProcessQFile();
Xvoid doswap();
Xvoid stractivate();
Xvoid MajorSwap();
Xvoid quiz();
Xvoid writescore();
Xvoid main();
X
X
X
X
Xvoid usage(s)
X  char *s;
X{
X  printf("Usage: %s datafile [-switch] [-ignorecase] [-help]\n\n", s);
X  printf("       -switch: questions and answers are switched\n");
X  printf("       -ignorecase: case doesn't matter\n");
X  printf("       -help: print this message (duh...)\n\n");
X  printf("        You can use just the first letter of each option as\n");
X  printf("        well.  datafile contains lines of the form\n");
X  printf("        question:answer (one on each line)\n");
X  exit(0);
X}
X
X/* parses arguments and sets the appropriate qstate elements.       */
X/* iff quiz was called with a filename, qstate.File is a non-empty  */
X/* string.  */
Xvoid parseargs(argc, argv)
X  int argc;
X  union {
X           char **args;
X           struct WBStartup *msg; 
X         } *argv;
X{
X  if (argc==0) { /* Workbench startup */
X    /*ns.DefaultTitle = argv->args[0];*/
X    if (argv->msg->sm_NumArgs > 2)
X      NonFatalError("%s: only one quiz file can be selected!\n");
X    if (argv->msg->sm_NumArgs >= 2)
X      strncpy(qstate.File, argv->msg->sm_ArgList[1].wa_Name,MAXFILENAME);
X   }
X  else { /* CLI startup */
X   int i, gotfname=0;
X   for (i=1; i<argc; i++) {
X
X    if ((!stricmp(argv->args[i], "-switch"))||
X        (!stricmp(argv->args[i], "/switch"))||
X        (!stricmp(argv->args[i], "-s")))
X        qstate.Switch = 1;
X
X    else if ((!stricmp(argv->args[i], "-help"))||
X        (!stricmp(argv->args[i], "/help"))||
X        (!stricmp(argv->args[i], "-h")))
X        usage(argv->args[0]); /* exits */
X
X    else if ((!stricmp(argv->args[i], "-ignorecase"))||
X        (!stricmp(argv->args[i], "/ignorecase"))||
X        (!stricmp(argv->args[i], "-i")))
X        qstate.CaseSensitive = 0;
X
X    else if ((!stricmp(argv->args[i], "-dpen")) && (i+1 < argc))
X        backnw.DetailPen = atoi(argv->args[++i]);
X
X    else if ((!stricmp(argv->args[i], "-bpen")) && (i+1 < argc))
X        backnw.BlockPen = atoi(argv->args[++i]);
X
X    else if ((!stricmp(argv->args[i], "-sdpen")) && (i+1 < argc))
X        ns.DetailPen = atoi(argv->args[++i]);
X
X    else if ((!stricmp(argv->args[i], "-sbpen")) && (i+1 < argc))
X        ns.BlockPen = atoi(argv->args[++i]);
X
X    else if (gotfname)
X        NonFatalError("Quiz: unrecognized option: %s.  Try -help\n",argv->args[i]);
X    else {
X        gotfname = 1;
X        strncpy(qstate.File, argv->args[i], MAXFILENAME);
X        } /* else */
X    } /* for i */
X  } /* CLI startup */
X} /* parseargs */
X
Xstruct ResListItem *MakeNewItem(ptr, size, type)
X   char *ptr;
X   int size, type;
X {
X   struct ResListItem *rtn;
X   extern char *AllocMem();
X   rtn = (struct ResListItem *)AllocMem(sizeof(struct ResListItem), MEMF_CLEAR);
X   if (rtn==NULL) {
X      fprintf(stderr, "Ummm.  Big trouble: couldn't allocate NewItem\n");
X      CleanUp(DONE_MSG);
X      exit(-1); /* machine is really low on memory anyway */
X      } /* if */
X   rtn->ptr = ptr;
X   rtn->size = size;
X   rtn->type = type;
X   return(rtn);
X} /* MakeNewItem */
X
Xvoid FreeResource(Item)
X  struct ResListItem *Item;
X{
Xswitch(Item->type) {
X  case ALLOC_TYPE:
X        if (Item->ptr) {
X          FreeMem(Item->ptr, Item->size);  /* the real free */
X          Item->ptr = NULL;
X          } /* if */
X	break;
X  case LOCK_TYPE:
X	UnLock((struct FileLock *)Item->ptr);
X	break;
X  case LIB_TYPE: /* these should be the last items released! */
X        if (Item->ptr) CloseLibrary(Item->ptr); /* cast it to what? */
X        break;
X  case SCREEN_TYPE: /* should be 2nd to last item released! */
X        if (Item->ptr) CloseScreen((struct Screen *)Item->ptr);
X        break;
X  case WINDOW_TYPE: /* must be closed AFTER screen! */
X        if (Item->ptr) CloseWindow((struct Window *)Item->ptr);
X        break;
X  default:
X	NonFatalError("FreeResource: unknown resource type: %ld\n",Item->type);
X	break;
X  } /* switch */
X} /* FreeResource */
X
X/* FreeAll will free all memory that has been allocated via Allocate() */
Xvoid FreeAll(ResListp)
X     struct List *ResListp;
X{
X  extern void ForgetMemory(); /* mymalloc.c */
X  struct ResListItem *node;
X  struct List TmpList;
X
X  /* Here's the deal: we want to free all memory resources.  We will
X     copy all non-memory resources into a temporary list, freeing up
X     the memory resources as we go along. */
X
X  NewList(&TmpList);
X
X  while (node = (struct ResListItem *)RemTail(ResListp)) {
X    if (node->type == ALLOC_TYPE) {
X       FreeResource(node); FreeMem(node, sizeof(struct ResListItem));
X       } /* if Alloc type */
X    else
X       AddTail(&TmpList, node);
X    } /* while */
X  /* this yields the original order */
X  NewList(ResListp);
X  while (node = (struct ResListItem *)RemTail(&TmpList))
X     AddTail(ResListp, node);
X
X  ForgetMemory();  /* must do this!!!  This lets mymalloc know to start
X                      allocating new memory and forget the old chunks  */
X
X} /* FreeAll */
X
X/* CleanUp keeps track of all resource allocations and will clean up
X   when passed DONE_MSG.  Must be passed INIT_MSG initally */
Xvoid CleanUp(msg, ptr, size)
X  int msg;
X  char *ptr;
X  int size;
X{
X  struct ResListItem *NewItem;
X  static struct List ResList;  /* this is the resource list */
X  struct ResListItem *node;
X
Xswitch (msg) {
X  case INIT_MSG:
X    NewList(&ResList); /* initialize resource list */
X    break;
X  case ALLOC_MSG:
X    /* first we have to allocate space for the NewItem.  If */
X    /* this call fails, we're in big trouble. */
X    NewItem = MakeNewItem(ptr, size, ALLOC_TYPE);
X
X    /* and add it to the list */
X    AddTail(&ResList, NewItem);
X    break;
X
X  case LOCK_MSG:
X    /* lock resource type */
X    NewItem = MakeNewItem(ptr, 0, LOCK_TYPE); /* size not used */
X    AddTail(&ResList, &(NewItem->n));
X    break;
X
X  case LIB_MSG:
X    /* library opened */
X    NewItem = MakeNewItem(ptr, 0, LIB_TYPE);
X    AddTail(&ResList, &(NewItem->n));
X    break;
X
X  case NEWSCREEN_MSG:
X    NewItem = MakeNewItem(ptr, 0, SCREEN_TYPE);
X    AddTail(&ResList, &(NewItem->n));
X    break;
X
X  case NEWWINDOW_MSG:
X    NewItem = MakeNewItem(ptr, 0, WINDOW_TYPE);
X    AddTail(&ResList, &(NewItem->n));
X    break;
X
X  case FREEONE_MSG:
X    NonFatalError("FREEONE not implemented yet!  Bogosity, dude.\n");
X    break;
X
X  case FREEALL_MSG:
X    FreeAll(&ResList);
X    break;
X
X  case DONE_MSG:  /* this is really cleaning up */
X    while (node = (struct ResListItem *)RemTail(&ResList)) {
X	/* this depends on the fact that &node = &NewItem->n */
X	FreeResource(node);
X	if (node) { 
X          FreeMem(node, sizeof(struct ResListItem)); /* overhead */
X          node = NULL;
X          } /* if */
X     }
X   break;
X  default:
X   NonFatalError("CleanUp: unknown message: %ld\n", msg);
X   break;
X  } /* switch */
X} /* CleanUp */
X
Xchar *Allocate(size)
X  int size;
X{
X  char *ptr;
X  extern char *AllocMem();
X
X/*  ptr = AllocMem(size, MEMF_CLEAR);*/
X  ptr = mymalloc(size);
X  if (ptr==NULL)
X      FatalError("Out of memory!  Couldn't allocate %ld bytes\n", size);
X/* already registered in mymalloc */
X#if 0
X  else CleanUp(ALLOC_MSG, ptr, size); /* register the resource */
X#endif
X  return(ptr);
X}
X
X/* NonFatalError */
Xvoid NonFatalError(s, a1, a2, a3, a4, a5)
X char *s, *a1, *a2, *a3, *a4, *a5;
X{
Xfprintf(stderr, s, a1, a2, a3, a4, a5);
X}
X
X/* prints error message string s (w/optional format strings) , cleans
X   up and exits */
Xvoid FatalError(s, a1, a2, a3, a4, a5)
X char *s, *a1, *a2, *a3, *a4, *a5;
X{
X  fprintf(stderr, s, a1 ,a2, a3, a4, a5);
X  CloseGraphics();
X  CleanUp(DONE_MSG);
X  exit(10);
X}
X
X/* read the file into one enormous buffer */
X/* returns 1 if successful, 0 if not      */
X
Xint readfile(name,thebuffer)
X    char *name, **thebuffer;
X{
X  struct FileInfoBlock *fib;
X  extern struct FileLock *Lock();
X  struct FileLock *lock;
X  struct FileHandle *fh;
X  int filesize,success,bytesread;
X
X  /* fib must be longword aligned!!! */
X  fib = (struct FileInfoBlock *)Allocate(sizeof(struct FileInfoBlock));
X#ifdef DEBUG
Xprintf("FileInfoBlock is at %d\n", fib);
X#endif
X  lock = Lock(name, ACCESS_READ);
X  if (!lock) {
X    NonFatalError("%s not found!\n", name);
X    return 0;
X    } /* if */
X  /* Normally, we could call
X     CleanUp(LOCK_MSG, (char *)lock, 0);
X     to register the resource; but we'll just unlock after we're
X     done so that the user can swap disks w/o trouble */
X  success = Examine(lock, fib);
X  if (!success) {
X      NonFatalError("Couldn't examine %s!\n", name);
X      UnLock(lock); /* we're done with the lock */
X      return 0;
X      }
X
X#ifdef DEBUG
X/*---------------------------------------------------------------*/
Xprintf("Name: %s\n", fib->fib_FileName);
Xprintf("[%d] ", fib->fib_DirEntryType);
Xif (fib->fib_DirEntryType>0) printf("Directory.\n");
X else printf("Plain file.\n");
Xprintf("file size in bytes:  %d\n", fib->fib_Size);
Xprintf("          in blocks: %d\n", fib->fib_NumBlocks);
Xprintf("file comment: %s\n", fib->fib_Comment);
X/*printf("Last modified: \n"); ShowDate(&(fib->fib_Date));*/
X/*---------------------------------------------------------------*/
X#endif
X
X  filesize = fib->fib_Size; /* file size */
X  UnLock(lock); /* we're done with the lock */
X
X  *thebuffer=Allocate(filesize+1); /* add a '\0' to end */
X
X  fh = Open(name, MODE_OLDFILE);
X  if (!fh) {
X    NonFatalError("Couldn't open %s\n", name);
X    return 0;
X    }
X  bytesread = Read(fh, *thebuffer, filesize);
X  if (bytesread != filesize) {
X    FatalError("Couldn't read %ld bytes from %s\n",filesize,name);
X    Close(fh);
X    return 0;
X    }
X  (*thebuffer)[filesize] = '\0';
X  Close(fh);
X  return 1;
X} /* readfile */
X
X/* count the number of lines that are separated by a newline in
X   buffer */
Xint countlines(buffer)
X    char *buffer;
X{
X  int count=0, i=0;
X  while (buffer[i]) if (buffer[i++]=='\n') count++;
X  if (buffer[i-1] != '\n') count++; /* printf("No terminating newline\n");*/
X   /*else printf("Terminating newline\n");*/
X  return(count);
X} /* countlines */
X
X/* returns 1 if s is in sl, 0 otherwise */
Xint isin(sl, s)
X   struct slist *sl;
X   char *s;
X{
X  if (qstate.CaseSensitive) 
X   while(sl) {
X     if (!strcmp(sl->s, s)) return(1);
X     sl=sl->next;
X    } /* while */
X  else 
X   while(sl) {
X     if (!stricmp(sl->s, s)) return(1);
X     sl=sl->next;
X    } /* while */
X  return(0);
X} /* isin */
X
X/* separate finds the ':' in a string "a:b" and replaces it with '\0'
X   then return a pointer to b.  Modifies s. */
Xchar *separate(s) char *s;
X{
X  while (*s) {
X   if (*s==':') { *s = '\0'; return(s+1);}
X   s++;
X   } /* while */
X  return(NULL); /* no ':' found */
X} /* separate */
X
X/* q is the question string */
Xvoid ask(q, sofar, total)
X char *q; 
X int sofar, total;
X{
X
X printf("[%ld/%ld] %s: ", sofar, total, q); }
X
X/* a is answer string */
Xvoid getinput(a) char *a; {
X
X gets(a); remwh(a); }
X
X/* if he gets it right */
Xvoid correct() { printf("Right!\n"); }
X
X/* if wrong */
Xvoid wrong(wrongi, right)
X   struct slist *right;
X   char *wrongi;
X{
X int num;
X
X/* if user just pressed return be nice; at least he admits his ignorance */
Xif (*wrongi) printf("Wrong, dummy.  ");
X num = slistSize(right);
X if (num == 1) printf("\"%s\"\n", right->s);
X else {
X   printf("\n");
X   while(right->next) printf("  \"%s\" or\n", right->s),right=right->next;
X   printf("  \"%s\"\n",right->s);
X   } /* else */
X} /* wrong */
X
X/* extractlines finds n strings each separated by a '\n'.  Creates and
X   fills an array that is the start address of each string.  Replaces
X   '\n' with '\0' */
Xchar **extractlines(n,bigbuf)
X  int n;
X  char *bigbuf;
X{
X  char **rtn;
X  int i=0, j=0;
X
X  rtn = (char **)Allocate(n * sizeof(char *));
X  rtn[0]=bigbuf;
X  while (bigbuf[i]) { /* j is in case there's no terminating newline */
X   if (bigbuf[i] == '\n') {
X     rtn[++j] = &(bigbuf[i])+1;
X     bigbuf[i] = '\0';
X    } /* if */
X   i++;
X  } /* while */
X
X  return(rtn);
X} /* extractlines */
X
X/* delete any blank lines in a buffer s */
Xvoid deleteblanklines(s)
X    char *s;
X{
X  char *t;
X  t = s;
X  /* get rid of any initial blank lines */
X  while (*t == '\n') t++;
X  while (1) {
X    /* t now points to first char in a non-empty line */
X    while (*t != '\n') {
X      if (*t == '\0') {*s = '\0'; return;}
X      *s++ = *t++;
X      }
X    while (*t == '\n') t++;
X    *s++='\n'; /* put one in */
X  } /* while 1 */
X} /* deleteblanklines */
X
X/* add an int to an ilist */
Xvoid ilistAdd(il, i)
X	ilist *il;
X	int i;
X{
X  while (il->next) il = il->next;
X  il->next = (ilist *)Allocate(sizeof(ilist));
X  (il->next)->i = i;
X  (il->next)->next = NULL;
X} /* ilistAdd */
X
X/* create and return a new ilist */
Xilist *Makeilist(i) int i;
X{
X  ilist *rtn;
X  rtn = (ilist *)Allocate(sizeof(ilist));
X  rtn->i = i; rtn->next = NULL;
X  return(rtn);
X} /* Makeilist */
X
X/***
X ***   This function should be called after a quiz file has been read
X ***   in via readfile().  The file will be in buf at that time.
X ***  
X ***   ProcessQFile will delete blank lines, count lines, and parse
X ***   them.  Note that all parameters are modified.  Chotto abunai ne.
X ***
X ***   *n is set to the number of lines.
X ***   *answers, *questions, *score, *origorder are all allocated.
X ***   A lot of other stuff will be allocated by subroutines.  When
X ***   this quiz file is to be discarded, all allocated memory should
X ***   be free via FreeAll().
X ***
X ***   Answers and questions will be swapped if qstate.Switch.  A
X ***   random order is calculated as well.
X ***
X ***   Returns the number of questions.
X ***
X ***/
Xint ProcessQFile(answers, questions, score, origorder, preprocess, numq)
X  struct slist ***answers, ***questions;
X  short **score, **origorder;
X  int preprocess, numq;
X{
X  /***
X   *** lines contains pointers to the beginning each line of
X   *** the quiz file.
X   *** tmpans is used in extracting the answer from each line.
X   ***
X   ***/
X  char **lines,*tmpans;
X  int n,i; /* n is the number of questions */
X
X  /***
X   *** tmpslist and tmpptr are used for swapping
X   ***/
X  struct slist *tmpslist;
X  short tmpshort;
X  if (preprocess) {
X    deleteblanklines(buf);
X    n = countlines(buf);
X 
X    lines = extractlines(n,buf);
X    if (qstate.GraphType = readscore(n)) cleargraph(),drawgraph();
X    UpdateMenuGraphState();
X    *answers = (struct slist **)Allocate(n * sizeof(struct slist *));
X    *questions = (struct slist **)Allocate(n * sizeof(struct slist *));
X    *score = (short *)Allocate(n * sizeof(short)); /* initializes to 0 */
X    *origorder = (short *)Allocate(n * sizeof(short));
X
X    history = *score; hindices = *origorder;
X    for (i=0; i<n; i++) {
X      (*origorder)[i]=(short)i;
X      /* now find the answer in "question:answer" line */
X      tmpans =  separate(lines[i]); /* modifies buf */
X
X      /* this error should really just abort this particular file
X         and not actually exit.  Later... */
X      if (!tmpans)
X        FatalError("Line \"%s\" is missing a colon\n", lines[i]);
X
X      (*questions)[i] = expand(lines[i]);
X      (*answers)[i] = expand(tmpans);
X
X    } /* for */
X
X    if (qstate.Switch) doswap(answers, questions);
X
X  } /* if preprocess */
X  else n = numq; /* if preprocess, n is passed in as last arg */
X
X  /* calculate a random order */
X  srand((unsigned)time(NULL)); /* time is in seconds; where's utime? */
X  for (i=0;i<n;i++) (*score)[i] = 0; /* can't be done in loop below */
X
X  for (i= 0; i<n; i++) {
X    int j, k;
X    /* swap questions j & k */
X    j=rand() % n; k = rand() % n;
X    if (j != k) {
X      tmpslist = (*questions)[j];
X      (*questions)[j] = (*questions)[k];
X      (*questions)[k] = tmpslist;
X
X      tmpslist = (*answers)[j];
X      (*answers)[j] = (*answers)[k];
X      (*answers)[k] = tmpslist;
X
X      tmpshort = (*origorder)[j];
X      (*origorder)[j] = (*origorder)[k];
X      (*origorder)[k] = tmpshort;
X      } /* if */
X    } /* for */
X  return(n);
X  } /* ProcessQFile */
X
X
Xvoid doswap(a,q)
X   struct slist ***a, ***q;
X{
X  struct slist **tmpptr;
X  tmpptr=*a;*a=*q;*q=tmpptr;
X} /* doswap */
X
Xvoid stractivate()
X{
X ActivateGadget(&sgadget, backwindow, NULL);
X} /* stractivate */
X
X/* swaps elements i and j of qs, as, ss, and os */
Xvoid MajorSwap(qs, as, ss, os, i, j)
X   short ss[], os[];
X   struct slist *as[], *qs[];
X   int i, j;
X{
X  short tmpshort;
X  struct slist *tmpslist;
X  
X  tmpslist = qs[i];
X  qs[i] = qs[j];
X  qs[j] = tmpslist;
X
X  tmpslist = as[i];
X  as[i] = as[j];
X  as[j] = tmpslist;
X
X  tmpshort = ss[i];
X  ss[i] = ss[j];
X  ss[j] = tmpshort;
X
X  tmpshort = os[i];
X  os[i] = os[j];
X  os[j] = tmpshort;
X} /* MajorSwap */
X
Xvoid quiz()
X {
X  /***
X   *** answers is a list of valid answers
X   *** questions--a list of valid questions
X   ***
X   *** tmpslist is used for swapping
X   ***
X   ***/
X  struct slist **answers, **questions;
X
X  /***
X   *** total is the total number of correct answers given.
X   *** n is the number of questions in the file.
X   *** gaveup = 1 if the user quits in the midde of this test.
X   ***   if this is the case, a score file will NOT be written
X   ***
X   ***/
X  int i=0,n=0,total=0,gaveup=0,j;
X
X  /* score is an array of shorts--one for each question.  Initially, 
X     score[i] = 0.  If the user answers a question right the first time,
X     score[i] = RIGHT.  If he gets it wrong, score is decremented and
X     the question is asked again later.  Each time he gets it wrong,
X     score[i] is decremented until score[i] == MAXWRONG.  If the user
X     gets it right, score[i] = -score[i] and thus tells how many times it
X     took to get it right. */
X
X  /***
X   *** origorder[n] is a list of shorts that gives the original index
X   *** of each quiz line.  Remember, these get shuffled around since
X   *** a random order is selected, and missed questions get stuck at
X   *** the end.
X   ***
X   ***/
X  short *score, *origorder;
X  ULONG val;
X
X  /* initialize values */
X  fileinit:
X  i=0,n=0,total=0,gaveup=0,val=IGNORE;
X
X  strcpy(sStringInfo.Buffer, "");
X  sStringInfo.BufferPos = 0;
X  sStringInfo.DispPos = 1;
X
X  /* if we are in an asking state, process the question file */
X  if (qstate.Asking)
X     n = ProcessQFile(&answers, &questions, &score, &origorder, 1, 0);
X
X
X  qtext = NULL; atext = NULL; ctext = NULL;
X  if (qstate.Asking)
X   {
X     qtext = questions[i]->s;
X    /* DrawQText();  necessary? we call redraw */
X   }
X
X UpdateCurrentQ(0, (int)origorder[i]);
X redraw();
X stractivate();
X
X/* main loop */
Xquizloop:
Xwhile ((i<n)||(!qstate.Asking)) {
X WaitPort(backwindow->UserPort); /* be nice */
X while (mes = GetMsg(backwindow->UserPort)) val=bhandlemsg(mes);
X 
X switch (val) {
X  case QUITNOW:
X   return; /* quit */
X
X  case ABORTNOW:
X   val = IGNORE;
X   OnMenu(backwindow, BEGINMENUNUM);
X   qstate.Asking = 0;
X   qstate.ShowingAns = 0;
X   qtext = NULL;
X
X   strcpy(sStringInfo.Buffer, "");
X   sStringInfo.BufferPos = 0;
X   sStringInfo.DispPos = 1;
X   redraw();
X   stractivate();
X   break;
X   /* abort */
X
X case OPENNEWFILE:
X   val=IGNORE;
X   qstate.Asking=1;
X   OffMenu(backwindow, BEGINMENUNUM);
X   goto fileinit; /* filename already set */
X
X case SWITCHEM:
X  doswap(&answers, &questions);
X  if (qstate.Asking) {
X    qtext = questions[i]->s;
X    DrawQText();
X   } /* if */
X   break; /* if switchem */
X
X case STARTASKING:
X if ((!qstate.Asking) && qstate.File[0]) {
X	qstate.Asking = 1;
X        i=0,total=0,gaveup=0,val=IGNORE;
X        OffMenu(backwindow, BEGINMENUNUM);
X        ProcessQFile(&answers, &questions, &score, &origorder, 0, n);
X        qtext = questions[i]->s;
X        UpdateCurrentQ(currentquestion, (int)origorder[i]);
X        DrawQText();
X        if (qstate.GraphType) drawgraph();
X        stractivate();
X  } /* if */
X  break; /* STARTASKING */
X
X case NEWSTRING:
X  if (qstate.Asking) {
X    remwh(atext);
X    if (isin(answers[i], atext)) {
X      /* correct();*/
X       if (score[i]==0) score[i] = RIGHT,total++;
X       else score[i] = -score[i];
X       if (qstate.ShowingAns) {
X         qstate.ShowingAns = 0;
X         EraseCText();
X         } /* if */
X      } /* if isin */
X
X    else { /* got this one wrong */
X    /* wrong(atext,answers[i]);*/
X     score[i]--;
X     if (score[i] <= MAXWRONG) {
X        i++;
X        ctext = NULL;
X        qstate.ShowingAns = 0;
X        EraseCText();
X        if (i>=n) break;
X        else
X         { /* do next question */
X          qtext = questions[i]->s;
X          DrawQText();
X          strcpy(sStringInfo.Buffer, "");
X          sStringInfo.BufferPos = 0;
X          sStringInfo.DispPos = 1;
X          RefreshGadgets(&sgadget, backwindow, NULL);
X          stractivate();
X          UpdateCurrentQ(currentquestion, (int)origorder[i]);
X          continue;
X         } /* else */
X
X     } /* give up on this question */
X     else  {
X       ctext = answers[i]->s; /* use the first legal answer I guess */
X       qstate.ShowingAns = 1;
X       DrawCText();
X       } /* else */
X
X     /* we want to ask again, so put the question back */
X     /* swap with the last element */
X     MajorSwap(questions, answers, score, origorder, i, n-1);
X     i--;
X     
X     } /* else wrong */
X
X    strcpy(sStringInfo.Buffer, "");
X    sStringInfo.BufferPos = 0;
X    sStringInfo.DispPos = 1;
X    i++;
X    if (i>=n) break;
X    qtext = questions[i]->s;
X    DrawQText();
X    UpdateCurrentQ(currentquestion, (int)origorder[i]);
X
X    } /* if asking */
X   break; /* if a new string */
X
X  case DISPLAYQ:
X   if (qstate.Asking) {
X     int theq=-1;
X     /* so we want to swap the current question with newquestion */
X     /* newquestion refers to the original order, so we have to find */
X     /* it first */
X     for (j=0; j<n; j++) if (origorder[j]==newquestion) theq=j;
X     if (theq==-1) {
X         NonFatalError("Couldn't find that question out of %d!\n",n);
X         break; /* from switch */
X         } /* if */
X
X     if (i== theq) break;
X     /* if theq<i, then we already asked this question and should decrement
X        i so that we don't lose any new questions */
X     if (theq<i) i--;
X     MajorSwap(questions, answers, score, origorder, i, theq);
X     qtext = questions[i]->s;
X
X     UpdateCurrentQ(currentquestion,(int)origorder[i]);
X
X     strcpy(sStringInfo.Buffer, "");
X     sStringInfo.BufferPos = 0;
X     sStringInfo.DispPos = 1;
X
X     redraw();
X     stractivate();
X
X   } /* if asking */
X   break; /* DISPLAYQ */
X  case IGNORE:
X   break;
X  default:
X   NonFatalError("quiz(): %d is an unknown message\n", val);
X   break;
X  } /* switch val */
X  
X stractivate();
X RefreshGadgets(&sgadget, backwindow, NULL);
X
X val=IGNORE;
X} /* while i<=n */
X
X  qstate.Asking = 0;
X  qstate.ShowingAns = 0;
X  qtext = NULL; /* so it won't be redraw()n */
X  OnMenu(backwindow, BEGINMENUNUM);
X  strcpy(sStringInfo.Buffer, "");
X  sStringInfo.BufferPos = 0;
X  sStringInfo.DispPos = 1;
X  RefreshGadgets(&sgadget, backwindow, NULL);
X  stractivate();
X
X  if (total != n) {
X    printf("You missed:\n");
X    for (i=0;i<n;i++)
X      /* score[i] is 0 if "quit" was selected */
X      if ((score[i] != RIGHT)&&(score[i] != 0))
X        printf("  %s: %s\n",questions[i]->s,answers[i]->s);
X    } /* if total != n */
X  else printf("Congratulations!\n");
X  printf("\nScore: %ld/%ld (%ld%%)\n", total, n, total * 100 / n);
X  if (gaveup==0){
X     writescore(score, n, origorder, total);
X     if (qstate.GraphType==0) { 
X        qstate.GraphType = BYQUESTION;
X        UpdateMenuGraphState();
X/*        OffMenu(backwindow, (SHIFTMENU(1)|SHIFTITEM(4)|SHIFTSUB(NOSUB)));
X        OnMenu(backwindow, (SHIFTMENU(1)|SHIFTITEM(3)|SHIFTSUB(NOSUB)));*/
X       } /* if */
X     } /* if */
X  if (qstate.GraphType) {cleargraph(); drawgraph();}
X  i = 0;
X  EraseQText(); EraseCText(); /* does nothing if they are empty */
X  goto quizloop;
X} /* quiz */
X
X/* write the score to filename.sc, also updates graph data */
Xvoid writescore(score, n, value, total)
X    short *score, *value;
X    int n, total;
X{
X  char scorefile[MAXFILENAME+3]; /* ".sc" */
X  FILE *fh;
X  int i, worstq;
X  strcpy(scorefile,qstate.File); strcat(scorefile,SCORE_FILE_EXT);
X  fh = fopen(scorefile,"a");
X  if (fh==NULL) NonFatalError("Couldn't open score file %s\n",scorefile);
X  else {
X    int *intptr;
X    intptr = AddTrial(n-total,n);
X
X    worstq = intptr[0];
X    for (i=0;i<n;i++) {
X/*printf("%d: orig=%d trialdata[%d]=%d worst=%d\n",
X    i,value[i],value[i],intptr[value[i]],worstq);*/
X      if (score[i] != RIGHT) {
X        fprintf(fh, "%d ", value[i]);
X        intptr[value[i]]++;
X        } /* if */
X      if (intptr[value[i]] > worstq) worstq = intptr[value[i]];
X      } /* for */
X    fprintf(fh, "\n");
X    fclose(fh);
X    /* only set worst question if he actually missed something */
X    if (n-total) SetWorstQuestion(worstq);
X    } /* else */
X
X} /* writescore */
X
Xvoid main(argc, argv)
X  int argc;
X  union {
X           char **args;
X           struct WBStartup *msg; 
X         } argv;
X{
X
X  CleanUp(INIT_MSG);		/* initialize resource manager */
X
X  qstate.Switch = 0;
X  qstate.File[0] = '\0';
X  qstate.ShowingAns = 0;
X  qstate.CaseSensitive = 1;
X  qstate.Asking = 0;
X  qstate.GraphType = 0;
X
X  parseargs(argc, &argv);
X
X  if (qstate.File[0]) {
X    if (readfile(qstate.File,&buf))
X       qstate.Asking = 1;
X    else strcpy(qstate.File, "");
X    } /* if */
X
X  InitGraphics();
X  redraw();
X
X  quiz();
X  CloseGraphics();
X  CleanUp(DONE_MSG);
X  exit(0);
X} /* main */
END_OF_FILE
if test 26723 -ne `wc -c <'quiz.c'`; then
    echo shar: \"'quiz.c'\" unpacked with wrong size!
fi
# end of 'quiz.c'
fi
if test -f 'screen.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'screen.c'\"
else
echo shar: Extracting \"'screen.c'\" \(17829 characters\)
sed "s/^X//" >'screen.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include <string.h> /* strcpy */
X#include "screen.h"
X
Xvoid DrawRaisedText();
Xvoid EraseQText(), EraseCText();
Xvoid DrawQText(), DrawCText();
Xvoid colorwindow();
Xint OpenLibs();
Xvoid InitGraphics();
Xvoid UpdateMenuGraphState();
Xvoid CloseGraphics();
XULONG ehandlemsg();
Xint handlegadget();
Xint handlebuttons();
Xint bhandlemsg();
Xint handlemenu();
Xvoid main();
Xvoid redraw();
Xvoid about(), abort();
Xint load();
X
Xextern void stractivate();             /* quiz.c  */
X
Xextern void drawgraph(), cleargraph(); /* graph.c */
Xextern int whichquestion();            /* graph.c */
X
X
X/* draw semi-3d-looking text */
Xvoid DrawRaisedText(rp,text,slen,x,y)
X struct RastPort *rp;
X  char *text;
X  int slen, x, y;
X {
X    /* draw it dark */
X     SetAPen(rp, GREY2);
X     Move(rp, x+1,y);
X     Text(rp, text, slen);
X
X     /* draw it light */
X     SetAPen(rp, GREY11);
X     Move(rp, x-1,y);
X     Text(rp, text, slen);
X
X     /* draw it just right */
X     SetAPen(rp, GREY7);
X     Move(rp, x,y);
X     Text(rp, text, slen);
X} /* DrawRaisedText */
X
X/* clear qtext display area */
Xvoid EraseQText() {
X  if (qpb[0])
X   { /* clear area */
X   SetAPen(rp, GREY7);
X   SetDrMd(rp, JAM1);
X   RectFill(rp, qpt[2], qpt[3], qpb[2], qpb[3]);
X   qpb[0] = 0;
X   } /* if */
X} /* EraseQText */
X
X#define QCOLOR GREY3
X/* draw the question */
Xvoid DrawQText() 
X{
X  int slen,theight,textsize;
X
X  /* delete old border */
X  if (qpb[0]) /* don't delete if first time */
X    {
X    /* clear the area */
X    SetAPen(rp, GREY7);
X    SetDrMd(rp, JAM1);
X    RectFill(rp, qpt[2], qpt[3], qpb[2], qpb[3]);
X    } /* if */
X  slen = strlen(qtext);
X  if (slen==0) return;
X  theight = rp->TxHeight;
X  textsize = slen * rp->TxWidth + 1; /* 1 pixel pad? */
X  qpt[0] = QX-BORDWIDTH*2;
X  qpt[1] = QY+theight+BORDWIDTH*2;
X  qpt[2] = QX-BORDWIDTH*2;
X  qpt[3] = QY-BORDWIDTH*2;
X  qpt[4] = QX+textsize+BORDWIDTH*2;
X  qpt[5] = QY-BORDWIDTH*2;
X
X  qpb[0] = QX-BORDWIDTH*2;
X  qpb[1] = QY+theight+BORDWIDTH*2;
X  qpb[2] = QX+textsize+BORDWIDTH*2;
X  qpb[3] = QY+theight+BORDWIDTH*2;
X  qpb[4] = QX+textsize+BORDWIDTH*2;
X  qpb[5] = QY-BORDWIDTH*2;
X
X  if (slen <= maxqchars) {
X     /* set border */
X     DrawBorder(rp,&qtop,0,0);
X     /* draw black box */
X     SetAPen(rp, QCOLOR);
X     SetDrMd(rp, JAM1);
X     RectFill(rp, qpt[2]+BORDWIDTH, qpt[3]+BORDWIDTH,
X                  qpb[2]-BORDWIDTH, qpb[3]-BORDWIDTH);
X     /* now draw text */
X     DrawRaisedText(rp, qtext, slen, QX, QY+baseheight);
X     } /* if */
X  else { /* must make bigger window */
X     char *lastspace = NULL, *s, *r;
X     int done = 0, numlines = 0, maxlen = 0, i;
X     /* Have to copy strings and output them after breaking up */
X     /* qtext.  We must know maxlen's value in order to draw   */
X     /* the dark grey rectangle                                */
X     char brokentext[MAXLINES][MAXQCHARS];
X     int sizes[MAXLINES];
X     r = qtext;
X
X     do {
X       numlines++;
X       s = r;
X       while (s<r+maxqchars) {
X        if (*s == ' ') lastspace = s;
X        else if (*s == '\0') {done = 1; break;}
X        s++;
X        } /* while */
X      /* this is gross */
X      /* we found a line */
X      if (lastspace) {
X        if (!done) *lastspace = '\0'; /* gulp */
X        slen = strlen(r); if (slen>maxlen) maxlen = slen;
X        strncpy(brokentext[numlines-1],r,slen);
X        sizes[numlines-1] = slen;
X        /*DrawRaisedText(rp, r, slen, QX, QY+baseheight+theight*(numlines-1));*/
X        if (!done) *lastspace = ' ';
X        r = lastspace + 1;
X        while (*r == ' ') r++; /* skip over any more spaces */
X        if (!*r) done = 1;
X        lastspace = NULL;
X        } /* if lastspace */
X      else if (done) /* reached the end in this iteration */
X       {
X        slen = strlen(r); if (slen>maxlen) maxlen = slen;
X        strncpy(brokentext[numlines-1],r,slen);
X        sizes[numlines-1] = slen;
X        /*DrawRaisedText(rp, r, slen, QX, QY+baseheight+theight*(numlines-1));*/
X       }
X      else { /* no breaks */
X        strncpy(brokentext[numlines-1],r,maxqchars);
X        sizes[numlines-1] = maxqchars;
X        /*DrawRaisedText(rp, r, maxqchars, QX, QY+baseheight+theight*(numlines-1));*/
X        r+=maxqchars; maxlen = maxqchars;
X        while (*r == ' ') r++; /* skip over any more spaces */
X        if (!*r) done = 1;
X        } /* else */
X    } while ((!done)&&(numlines<MAXLINES));
X     /* set border -- only 3 must be changed*/
X     textsize = maxlen * rp->TxWidth + 1; /* 1 pixel pad? */
X
X     qpt[4] = QX+textsize+BORDWIDTH;
X     qpb[2] = QX+textsize+BORDWIDTH;
X     qpb[4] = QX+textsize+BORDWIDTH;
X
X     qpt[1] = QY+theight*numlines+BORDWIDTH;
X     qpb[1] = QY+theight*numlines+BORDWIDTH;
X     qpb[3] = QY+theight*numlines+BORDWIDTH;
X
X     DrawBorder(rp,&qtop,0,0);
X     /* draw black box */
X     SetAPen(rp, QCOLOR);
X     SetDrMd(rp, JAM1);
X     RectFill(rp, qpt[2]+BORDWIDTH, qpt[3]+BORDWIDTH,
X                  qpb[2]-BORDWIDTH, qpb[3]-BORDWIDTH);
X
X    /* now actually draw the text */
X    for (i=0;i<numlines;i++)
X     DrawRaisedText(rp, brokentext[i], sizes[i], QX, QY+baseheight+theight*i);
X  } /* else */
X} /* DrawQText */
X
X/* clear ctext display area */
Xvoid EraseCText() {
X  if (cpb[0])
X   { /* clear area */
X   SetAPen(rp, GREY7);
X   SetDrMd(rp, JAM1);
X   RectFill(rp, cpt[2], cpt[3], cpb[2], cpb[3]);
X   cpb[0] = 0;
X   } /* if */
X} /* EraseCText */
X
X#define CCOLOR 15 /* ? */
X/* draw the correct answer */
Xvoid DrawCText()
X{
X  int slen, theight, textsize;
X  /* delete old border if existing */
X  if (cpb[0])
X   { /* clear area */
X   SetAPen(rp, GREY7);
X   SetDrMd(rp, JAM1);
X   RectFill(rp, cpt[2], cpt[3], cpb[2], cpb[3]);
X   } /* if */
X  if (ctext==NULL) return;
X  slen = strlen(ctext);
X  if (slen==0) return;
X  theight = rp->TxHeight;
X  textsize = slen * rp->TxWidth + 1; /* 1 pixel pad? */
X
X  cpt[0] = CX-BORDWIDTH*2;
X  cpt[1] = CY+theight+BORDWIDTH*2;
X  cpt[2] = CX-BORDWIDTH*2;
X  cpt[3] = CY-BORDWIDTH*2;
X  cpt[4] = CX+textsize+BORDWIDTH*2;
X  cpt[5] = CY-BORDWIDTH*2;
X
X  cpb[0] = CX-BORDWIDTH*2;
X  cpb[1] = CY+theight+BORDWIDTH*2;
X  cpb[2] = CX+textsize+BORDWIDTH*2;
X  cpb[3] = CY+theight+BORDWIDTH*2;
X  cpb[4] = CX+textsize+BORDWIDTH*2;
X  cpb[5] = CY-BORDWIDTH*2;
X
X  if (slen <= maxqchars) {
X     /* set border */
X     DrawBorder(rp,&ctop,0,0);
X     /* draw black box */
X     SetAPen(rp, CCOLOR);
X     SetDrMd(rp, JAM1);
X     RectFill(rp, cpt[2]+BORDWIDTH, cpt[3]+BORDWIDTH,
X                  cpb[2]-BORDWIDTH, cpb[3]-BORDWIDTH);
X     /* now draw text */
X     DrawRaisedText(rp, ctext, slen, CX, CY+baseheight);
X     } /* if */
X    else { /* must make bigger window */
X     char *lastspace = NULL, *s, *r;
X     int done = 0, numlines = 0, maxlen = 0, i;
X     /* Have to copy strings and output them after breaking up */
X     /* ctext.  We must know maxlen's value in order to draw   */
X     /* the dark grey rectangle                                */
X     char brokentext[MAXLINES][MAXQCHARS];
X     int sizes[MAXLINES];
X     r = ctext;
X
X     do {
X       numlines++;
X       s = r;
X       while (s<r+maxqchars) {
X        if (*s == ' ') lastspace = s;
X        else if (*s == '\0') {done = 1; break;}
X        s++;
X        } /* while */
X      /* this is gross */
X      /* we found a line */
X      if (lastspace) {
X        if (!done) *lastspace = '\0'; /* gulp */
X        slen = strlen(r); if (slen>maxlen) maxlen = slen;
X        strncpy(brokentext[numlines-1],r,slen);
X        sizes[numlines-1] = slen;
X        if (!done) *lastspace = ' ';
X        r = lastspace + 1;
X        while (*r == ' ') r++; /* skip over any more spaces */
X        if (!*r) done = 1;
X        lastspace = NULL;
X        } /* if lastspace */
X      else if (done) /* reached the end in this iteration */
X       {
X        slen = strlen(r); if (slen>maxlen) maxlen = slen;
X        strncpy(brokentext[numlines-1],r,slen);
X        sizes[numlines-1] = slen;
X       }
X      else { /* no breaks */
X        strncpy(brokentext[numlines-1],r,maxqchars);
X        sizes[numlines-1] = maxqchars;
X        r+=maxqchars; maxlen = maxqchars;
X        while (*r == ' ') r++; /* skip over any more spaces */
X        if (!*r) done = 1;
X        } /* else */
X    } while ((!done)&&(numlines<MAXLINES));
X     /* set border -- only 3 must be changed*/
X     textsize = maxlen * rp->TxWidth + 1; /* 1 pixel pad? */
X
X     cpt[4] = CX+textsize+BORDWIDTH;
X     cpb[2] = CX+textsize+BORDWIDTH;
X     cpb[4] = CX+textsize+BORDWIDTH;
X
X     cpt[1] = CY+theight*numlines+BORDWIDTH;
X     cpb[1] = CY+theight*numlines+BORDWIDTH;
X     cpb[3] = CY+theight*numlines+BORDWIDTH;
X
X     DrawBorder(rp,&ctop,0,0);
X     /* draw black box */
X     SetAPen(rp, CCOLOR);
X     SetDrMd(rp, JAM1);
X     RectFill(rp, cpt[2]+BORDWIDTH, cpt[3]+BORDWIDTH,
X                  cpb[2]-BORDWIDTH, cpb[3]-BORDWIDTH);
X
X    /* now actually draw the text */
X    for (i=0;i<numlines;i++)
X     DrawRaisedText(rp, brokentext[i], sizes[i], CX, CY+baseheight+theight*i);
X  } /* else */
X} /* DrawCText */
X
X/* fills in an entire window with color c */
Xvoid colorwindow(w,c)
X  struct Window *w;
X  int c;
X{
X  SetDrMd(w->RPort, JAM1);
X  SetAPen(w->RPort, c);
X  RectFill(w->RPort, 0, 0, w->Width, w->Height);
X} /* colorwindow*/
X
X#if 0
Xvoid FatalError(s) char *s;
X{
X printf("%s", s);
X CloseGraphics();
X exit(1);
X} /* FatalError */
X#endif
X
X/* Just opens intuition library.  Returns 1 if successful, 0 if not */
Xint iopen=0, gopen=0;
Xint OpenLibs() {	
X  IntuitionBase = (struct IntuitionBase *)
X    OpenLibrary("intuition.library", 0);
X  if (IntuitionBase==NULL) return(0);
X  iopen = 1;
X  GfxBase = (struct GfxBase *)
X    OpenLibrary("graphics.library", 0);
X  if (GfxBase==NULL) return(0);
X  gopen=1;
X  return(1);
X  } /* OpenLibs */
X
Xvoid InitGraphics() {
X if (OpenLibs()==0) FatalError("Couldn't open libraries!\n");
X
X if ((qscreen = OpenScreen(&ns)) == NULL)
X   FatalError("Couldn't open screen!\n");
X vp = &(qscreen->ViewPort);
X
X /* initialize colortable */
X LoadRGB4(vp, &mycmap[0], 32);
X
X/* errnw.Screen  = qscreen;*/
X backnw.Screen = qscreen;
X
X backnw.FirstGadget = &sgadget;
X if ((backwindow = OpenWindow(&backnw)) == NULL)
X   FatalError("Couldn't open backdrop window!\n");
X/* if ((ewindow = OpenWindow(&errnw)) == NULL)
X   FatalError("Couldn't open error window!\n");*/
X
X menustrip[0] = &m1;
X menustrip[1] = &m2;
X menustrip[0]->NextMenu = menustrip[1];
X SetMenuStrip(backwindow,menustrip[0]);
X  
X if (qstate.Asking || (!qstate.File[0]))
X   OffMenu(backwindow, BEGINMENUNUM);
X if (qstate.GraphType==BYTRIAL) {
X   OffMenu(backwindow, TRIALMENUNUM);
X   OnMenu(backwindow, QUESTMENUNUM);
X   } /* if */
X else if (qstate.GraphType==BYQUESTION) 
X {
X   OffMenu(backwindow, QUESTMENUNUM);
X   OnMenu(backwindow, TRIALMENUNUM);
X   } /* if */
X ShowTitle(qscreen, FALSE);
X rp = backwindow->RPort;
X
X /* calculate how many characters can fit in one line of text */
X /* note that this method doesn't account for variable-width fonts :-( */
X maxqchars = (MAXQX-QX)/rp->TxWidth;
X baseheight = rp->TxBaseline;
X } /* InitGraphics */
X
X/* This is not very clean, but we need to be able to turn off/on the
X   correct menu items depending of the state of the graph */
Xvoid UpdateMenuGraphState() {
X if (qstate.GraphType==BYTRIAL) {
X   OffMenu(backwindow, TRIALMENUNUM);
X   OnMenu(backwindow, QUESTMENUNUM);
X } /* if */
X else if (qstate.GraphType == BYQUESTION) {
X   OffMenu(backwindow, QUESTMENUNUM);
X   OnMenu(backwindow, TRIALMENUNUM);
X } /* if */
X} /* UpdateMenuGraphState */
X
Xvoid CloseGraphics()
X{
X  if (backwindow) {
X    ClearMenuStrip(backwindow);
X    while (mes = GetMsg(backwindow->UserPort)) ReplyMsg(mes);
X    CloseWindow(backwindow);
X  } /* if backwindow */
X  if (qscreen) CloseScreen(qscreen);
X  if (gopen) CloseLibrary(GfxBase);
X  if (iopen) CloseLibrary(IntuitionBase);
X  /*OpenWorkBench();*/
X  /*WBenchToFront();*/
X} /* CloseGraphics */
X
XULONG ehandlemsg(message)
X   struct IntuiMessage *message;
X{
X  struct IntuiMessage localms;
X  int i;
X  UBYTE *s, *d;
X  ULONG class,code;
X
X  s = (UBYTE *)message;
X  d = (UBYTE *)&localms;
X
X  for (i=0;i<sizeof(struct IntuiMessage);i++) *d++=*s++;
X  ReplyMsg(message);
X    
X  class = localms.Class;
X  code = localms.Code;
X  switch (class)
X   {
X     case CLOSEWINDOW:
X        break;
X     case REFRESHWINDOW:
X     case NEWSIZE:
X       redraw();
X     default:
X       break;
X   } /* switch */
X   return(class);
X} /* ehandlemsg */
X
Xint handlegadget(g)
X   struct Gadget *g;
X{
X  switch (g->GadgetID) {
X    case STRINGID:
X      atext = ((struct StringInfo *)(g->SpecialInfo))->Buffer;
X      /*DrawQText();*/
X      return(NEWSTRING);
X      break;
X    case IMAGEID:
X      break;
X    default:
X      break;
X    }
X     return(IGNORE);
X
X} /* handle gadget */
X
Xvoid about() { printf("Quiz by Curtis Eubanks\n"); }
X
X/* returns 1 if successful, 0 if not */
Xint load() { 
X  if (*sStringInfo.Buffer)
X  {
X    strcpy(qstate.File, sStringInfo.Buffer);
X    history = hindices = (short *)NULL;
X    CleanUp(FREEALL_MSG);
X    if (readfile(qstate.File, &buf)) {
X      qstate.Asking = 1;
X      return 1;
X      } /* if */
X  } /* if */
X  return 0;
X} /* load */
X
Xvoid abort() {
X } /* abort */
Xint handlemenu(code)
X  ULONG code;
X{
X  ULONG MenuNumber = MENUNUM(code), ItemNumber = ITEMNUM(code);
X  if ((code != MENUNULL) && (ItemNumber != NOITEM)) {
X   if (MenuNumber == PROJMENU) {
X     switch (ItemNumber)
X       {
X         case QUITNUM:
X            return(QUITNOW);
X            break;
X         case ABOUTNUM:
X            about();
X            break;
X         case LOADNUM:
X            if (load()) return(OPENNEWFILE); else return(IGNORE);
X            break;
X         case BEGINNUM:
X            return(STARTASKING);
X            break;
X         default:
X            NonFatalError("Unknown Project Menu selection!\n");
X            break;
X         } /* switch */
X    } /* if project menu */
X  else if (MenuNumber==OPTMENU) {
X   switch (ItemNumber)
X     {
X       case SWITCHNUM:
X         return(SWITCHEM);
X         break;
X       case IGNORENUM:
X         qstate.CaseSensitive = !qstate.CaseSensitive;
X         break;
X       case ABORTNUM:
X         abort();
X         return(ABORTNOW);
X         break;
X       case MENUBYTRIAL:
X         if (qstate.GraphType != BYTRIAL && qstate.GraphType) {
X            qstate.GraphType = BYTRIAL;
X            OffMenu(backwindow, TRIALMENUNUM);
X            OnMenu(backwindow, QUESTMENUNUM);
X            cleargraph();
X            drawgraph();
X            stractivate();
X            }
X         return IGNORE;
X         break;
X       case MENUBYQUESTION:
X         if (qstate.GraphType != BYQUESTION && qstate.GraphType) {
X            qstate.GraphType = BYQUESTION;
X            OffMenu(backwindow, QUESTMENUNUM);
X            OnMenu(backwindow, TRIALMENUNUM);
X            cleargraph();
X            drawgraph();
X            stractivate();
X            }
X         return IGNORE;
X         break;
X       default:
X         NonFatalError("Unknown Option Menu selection!\n");
X         break;
X     } /* switch itemnumber */
X    } /* if option menu */
X  } /* if non-null menu selection */
X return(IGNORE);
X} /* handlemenu */
X
X/* handle button presses */
Xint handlebuttons(code, mousex, mousey)
X  ULONG code;
X  SHORT mousex, mousey;
X{
X  if (code == SELECTDOWN) {
X    int q;
X#ifdef DEBUG
X    printf("mouse at %d, %d\n", mousex, mousey);
X#endif
X    if ((qstate.GraphType == BYQUESTION) &&
X        ((q=whichquestion(mousex, mousey))!=-1))
X       { /* in graph.c */
X         newquestion = q; /* global, yucky */
X         return DISPLAYQ;
X       } /* if q != -1 */
X    } /* if code */
X  return IGNORE;
X} /* handlebuttons */
X
X
X
Xint bhandlemsg(message)
X   struct IntuiMessage *message;
X{
X  struct IntuiMessage localms;
X  int i;
X  UBYTE *s, *d;
X  ULONG class,code;
X
X  s = (UBYTE *)message;
X  bloop:
X  d = (UBYTE *)&localms;
X
X  /* copy message */
X  for (i=0;i<sizeof(struct IntuiMessage);i++) *d++=*s++;
X  ReplyMsg(message);
X  
X  class = localms.Class;
X  code = localms.Code;
X  switch (class)
X   {
X     case REFRESHWINDOW: /* ugh.  this hurts */
X        /* any more REFRESHWINDOW msgs waiting? */
X        if (mes = GetMsg(backwindow->UserPort))
X        {  /* don't redraw if more than one REFRESHWINDOW in a row */
X/*          printf("waiting: %d ",mes->Class);
X            if (mes->Class==REFRESHWINDOW) printf("(refresh)\n");
X            else printf("(not refresh)\n");*/
X            s = (UBYTE *)mes; message = mes; /* legal? */
X            if (mes->Class==REFRESHWINDOW) goto bloop;
X            else { /* some other message.  Redraw and process it */
X                   redraw();
X                   goto bloop;
X                 } /* else */
X        } /* if */
X        else redraw();
X        break;
X     case GADGETUP:
X        return(handlegadget((struct Gadget *)localms.IAddress));
X        break;
X     case MENUPICK:
X        return(handlemenu(code));
X        break;
X     case MOUSEBUTTONS:
X        return(handlebuttons(code, localms.MouseX, localms.MouseY));
X	break;
X     default:
X        break;
X   } /* switch */
X   return(IGNORE);
X} /* bhandlemsg */
X
X#if 0
Xvoid main(argc, argv) 
X   int argc;
X   union {
X           char **args;
X           struct WBStartup *msg; 
X         } argv;
X{
X  ULONG val=0;
X  if (argc>2) {
X/*    errnw.DetailPen = atoi(argv.args[1]);
X    errnw.BlockPen = atoi(argv.args[2]);*/
X  }
X  if (argc>4) {
X    ns.DetailPen = atoi(argv.args[3]);
X    ns.BlockPen = atoi(argv.args[4]);
X  }
X
X  InitGraphics();
X
X  redraw();
X   /* WaitPort(ewindow->UserPort);*/
X  while(1) {
X   while (mes = GetMsg(backwindow->UserPort)) val=bhandlemsg(mes);
X   if (val == QUITNUM) break;
X/*   while (mes = GetMsg(ewindow->UserPort)) val=ehandlemsg(mes);*/
X   if (val == CLOSEWINDOW) break;
X   }
X  CloseGraphics();
X} /* main */
X#endif
X#define BaseX 30
X#define BaseY 40
X#define Bsize 12
X
Xvoid redraw() {
X   colorwindow(backwindow, GREY7);
X   SetDrMd(rp, JAM1);
X
X   /* draw gadget's border */
X   DrawBorder(rp,&ibt,0,0); 
X   if (qtext) DrawQText();
X   if (qstate.ShowingAns) DrawCText();
X   if (qstate.GraphType) drawgraph();
X   RefreshGadgets(&sgadget, backwindow, NULL);
X } /* redraw */
END_OF_FILE
if test 17829 -ne `wc -c <'screen.c'`; then
    echo shar: \"'screen.c'\" unpacked with wrong size!
fi
# end of 'screen.c'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@uunet.uu.net>.
Mail comments to the moderator at <amiga-request@uunet.uu.net>.
Post requests for sources, and general discussion to comp.sys.amiga.misc.