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.