         Q L   H A C K E R ' S   J O U R N A L
      ===========================================
           Supporting  All  QL  Programmers
      ===========================================
         #18                        August 1994
      
    The QL Hacker's Journal (QHJ) is published by Tim
Swenson as a service to the QL Community.  The QHJ is
freely distributable.  Past issues are available on disk,
via e-mail, or via the Anon-FTP server, garbo.uwasa.fi. 
The QHJ is always on the look out for article submissions.

        QL Hacker's Journal
     c/o Tim Swenson
     5615 Botkins Rd 
     Huber Heights, OH 45424 USA
     (513) 233-2178
     *** swensotc@ss2.sews.wpafb.af.mil
     tswenson@dgis.dtic.dla.mil

     *** Note new E-Mail address


EDITOR'S FORUMN

   This is the second issue that is far later that I would
like.  Let me explain the reason why.  Back in March I was
told that I was being matrixed to do computer support for
the B-2 Bomber Systems Program Office.  What this job
entails is managing a computer and telecommunications
branch.  I have three government civilians and nine
contractors.  I have a total budget of about 7 million
dollars (most of that is already obligated to fund telephone
circuits and the contractors).  I am the only Captain branch
chief, so I get lots of visibility.  I don't deal with
anything about the plane itself, my branch supports the
office automation for the Air Force office that manages the
B-2 program.

   Needless to say I have been busy figuring out my new job
and what I am doing.  I only had a 1 week overlap with the
person I replaced.  Nine hour days are getting to be the
norm for me.

   I will still keep working on this newsletter.  I hope
that some people will send in some articles and take the
pressure off of me (hint, hint).  Thanks to all that are
still out there waiting for an issue.

   Now on to other things:

   This past May I made the trek to Rhode Island.  Paul
Holmgren was good enough to give me a ride.  We left Friday
night and drove straight through to Rhode Island (NoDoz -
don't drive 12 hrs without it).  Paul has this nice little
black box that sets off radar detectors.  I did a little
behavior modification on some speeders on I-95.

   The show in Rhode Island was pretty good.  Saw a few
faces that I have not seen for a while.  I won't go into
much detail since other QL sources cover it better than I
could.  There was not much new on the programming front. 
C68 version 3.14c was released just before the show.  Al
Boehm had a nice cloud simulator running on the QXL.  I'm
looking forward to it's public release.  I did pick up some
copies of two older compilers; Computer One Pascal and
ProFortran 77.  ProFortran 77 had a bad microdrive, but I
found someone that has a good copy.  I should get it soon. 
Computer One Pascal reminds me of TurboPascal.  It's got
everything in one package, editor, compiler, linker.  I'll
have to give it a test drive soon.

   While in Rhode Island, I decided to actually buy some new
software for my QL.  I've been interested in the Pointer
Environment for a while, so I picked up QPAC II.  Now I need
a mouse, so I bought SERMouse.  But, SERMouse has a problem
with the standard QL serial port (as buggy as it is).  So I
bought the HERMES chip.  Don Walterman had a $10 2400 baud
modem, so I bought it and doubled my modem capability.  I've
moved my Hayes 1200 Personal modem (a modem that looks like
a wall transformer) to Z88 duty.  With a nice convertor
cable, it works fine.

   Now I have enough software to require a boot disk.  I put
all of the software listed above, plus a few other utilities
on one disk and created a boot program.  I saw one that John
Impellizzeri had and copied it.  When software is loaded on
the QL, it's done in silence.  On an MS-DOS system, each
package usually prints something to the screen letting the
user know it's loading.  This is fairly handy when trying
to debug stuff, so I did the same on the QL.

   I open a little window and print a line stating what
application is loading.  This shows a progression of
software being loaded.  Then when it's all done, I just do
a new.  Now the QL is loaded, SuperBasic is empty, and
ready to go.

   The only problem I have is QEM clashing with SERMouse. 
Both are trying to get the SER2 port.  Since I have
SERMouse running as a job, I just RJOB it before running
QEM.  The only hard part is getting behind the QL to switch
the modem cable with the serial mouse.

   As for QL Internet news, I recently FTPed a copy of ELVIS
from maya.dei.unipd.it.  ELVIS is a clone of the Unix
editor, VI.  I've seen it listed by some QL PD distributers,
but it took a while for it to hit the net.  I've unzipped
the executable and it looks and feels like VI.  Not that I'm
a big fan of VI, it just nice to have another editor that is
fairly portable.

   I also downloaded MINEFIELD, a Freeware Pointer
Environment program, by Philipe Trion that is a clone of
the MS-Windows game, Mine Sweeper.  Besides QPAC II,
MINEFIELD is the first PE program that I ran.  It was a
great demo of the PE.  Maybe it should be distributed with
other PE starter packages like QPAC II.   My wife got hooked
on it and stayed up to 1 AM playing it.  Now if someone
would do a PENTE program for the PE.

   And the last thing.  The last weekend in August is
ComputerFest, here in Dayton.  I plan on helping man the
Computer Museum booth with Gary Ganger.  But the important
thing is that I'll be hosting the 2nd Annual QHJ Bar-B-Q at
my house.  Like last year, I'll provide burgers, hot dogs,
plates, etc.  People will probably need to bring drinks
(unless someone brings a few 2 litre bottles).  I'll have
directions at the Fest.  Just drop by the Sinclair booths.

   Until next time, Happy Hacking.


COMPLEX ASCII ROTATION
   
   While reading one of the many computer magazines that I
read in a month (got to have something to do while I eat my
lunch), I read an article in which the columnist talked
about security and mail.  He was commenting about how open
Internet mail (and e-mail in general) is fairly open and
easy to tap into.

   Simple Mail Transfer Protocol (SMTP), the Internet's
mail protocol, only handles ASCII mail.  Most e-mail is sent
in the clear, no encryption.  Since a mail message can pass
through many different sites, it can be copied and read at
almost any point.

   Having been a Unix system administrator, I know how mail
can bounce and be sent to the "Postmaster" for resolution. 
As "Postmaster" I read other persons mail to figure out
where it was supposed to go  I like to tell people that
e-mail is about as private as a post card.  You don't write
very private stuff on a post card, so don't do it with
e-mail.

   Another general rule of e-mail is not to write anything
in a mail message that you would not like to see on the
front page of your local newspaper.

   I got to thinking about using encryption for e-mail (
encryption is a hot topic these days).  But, I did not want
to go the the extremes of using Public-Key encryption or
DES.  Since the whole idea is to make your mail unreadable
to the general perusal, a fairly simple algorithm would
work.

   I also wanted something that could be very easy to port
and was not dependent on any computer platform.  So, the
whole scheme had to rely heavily on the password the user
uses.

   Ok, now to the details.  What this program does is more
than just simple rotation of the characters.  Simple
rotation is just adding a constant number to all
characters. If you rotated by 2, A becomes C, B becomes D,
etc.  Way too simple.  So to mix it up, a rotation array is
made out of the password.  For each letter of the password,
a MOD 7 is done and the results put into the array.  The
longer the password the longer the array.  You could make
the program do simple rotation with a one character
password.

   Once the array is created, the program goes through the
input file, using the rotation array to rotate a number of
characters.  The program will cycle through the rotation
array many times (like a circular queue) until it reaches
the end of the input file.  The output file has the general
look of the input file (newlines are not touched), but the
words are now meaningless.

   This encryption is not unbreakable, but to 99% of the
population it is unreadable.  Someone would have to bepretty determined to try to unencrypt it.  

   Since this was a fairly simple program to write, I wrote
a version in SuperBasic and C.  When I get my Fortran
compiler up and running, I'll try to port it to Fortran. 
Then I'll try Pascal to try out Computer One Pascal.

100 OPEN #3,con_250x150a50x50
110 PAPER #3,0 : INK #3,4 : BORDER #3,4 : CLS #3
120 INPUT #3,"Name of Input File : ";in_file$
130 INPUT #3,"Name of Output File : ";out_file$
140 INPUT #3,"Password : ";password$
150 INPUT #3,"Rotate or Unrotate (U/R) : ";rot$
160 IF rot$="r" THEN rot$="R"
170 IF rot$="u" THEN rot$="U"
180 IF rot$<>"U" AND rot$<>"R" THEN GO TO 150
190 DIM rot(30)
200 REMark Create Rotation Array
210 pass_len = LEN(password$)
220 FOR x = 1 TO pass_len
230    rot(x) = CODE(password$(x)) MOD 7
240 NEXT x
250 OPEN_IN #4,in_file$
260 OPEN_NEW #5,out_file$
270 rot_mark = 1
280 REPeat loop
290 IF NOT EOF(#4) THEN 
300    in$ = INKEY$(#4,-1)
310   ELSE 
320    EXIT loop
330 END IF 
340      IF CODE(in$) < 32 THEN 
350         PRINT #5,in$;
360      ELSE 
370         LET temp = CODE(in$)
380         IF rot$="R" THEN temp = temp+rot(rot_mark)
390         IF rot$="U" THEN temp = temp-rot(rot_mark)
400         PRINT #5,CHR$(temp);
410      END IF 
420      rot_mark = rot_mark + 1
430      IF rot_mark > pass_len THEN rot_mark = 1
440  END REPeat loop
450 CLOSE #4 : CLOSE #5
460 PRINT #3,"  Done "
470 CLOSE #3

/* Complex ASCII Rotation

   This program takes as input a password and
an ASCII file.  From the password a rotation
queue is derived.  Then the incomming file is
processed using the rotation queue to rotate
each character differently then those to it's
left and right.  The end result is an output
file with the rotated text.

The program also reverses the process and will
produce the original text out of the rotated
file.

*/

#define  ROTATE     1
#define  UNROTATE   0

#include <stdio_h>

/*  Global Array for holding Rotation Queue */
int rot_array[30];

main ()
{

   int c, i, fd1, fd2, rot_mark, pass_len, rot;
   char *password;


   printf("Enter the Input File : ");
   fd1 = open_file("r");

   printf("Enter the Output File : ");
   fd2 = open_file("w");

   printf("Enter a Password : ");
   gets(password);
   pass_len = strlen(password);

/* generate the rotation array from the password */
   for ( i=1; i<=pass_len; i++)
      rot_array[i] = password[i] % 7;

   printf("Rotate or Unrotate (U/R) : ");
   c = getchar();
   putchar(c);
   printf("\n");
   if ( c == 'R' || c == 'r' )
      rot = ROTATE;
   else
      rot = UNROTATE;

/* Start of the main part of the program */
   rot_mark = 1;

   while (( c=getc(fd1)) != EOF)
   {
      if ( !isprint(c) )
         putc(c,fd2);
      else {
         if ( rot == ROTATE )
            c = c + rot_array[rot_mark];
         else
            c = c - rot_array[rot_mark];
         putc(c,fd2);
         rot_mark++;
         if ( rot_mark > pass_len )
            rot_mark = 1;
      }
   }
   printf("\n     Done!\n\n");
}

/*  This procedure gets a file name and opens it.
if it fails, it aborts the program.
It takes three values "r", "w", "a"
  for Read, Write, Append.

  Usage:   file_pointer = open_file("r");
 */
open_file(rwa)
  char *rwa;
{

   char filename[30];
   int  fd;

   gets(filename);
   fd = fopen(filename, rwa);
   if ( fd == NULL) {
      printf("\n   Error Opening File! \n");
      abort(-1);
   }

   return fd;
}


APPROXIMATE STRING MATCHING

   In past issues, string matching has been a reaccuring
theme.  In the April 1994 issue of "C Users Journal" there
was an article on string matching that uses a different
algorithm for guessing how close two strings are; Frequency
Distribution.

   Frequency Distribution counts how many occurances of each
letter appear in the word.  For "test", t=2, e=s=1.  For
"shasta", s=a=2, h=t=1.  To guess how close two words are
the frequency distribution is compared.  If a word has
about the same frequency distribution, then it is assumed to
be equal.

   There lies the weakness of this algorithm.  The author
states that anagrams, like "BAT" and "TAB", would appear to
be the same, since they have the same frequency
distribution, where they are not.  I think this can be
extended to all words that are spelled differently but have
the same letters in them.  A simple example would be "TEA"
and "ATE".  They are not an anagram, but they have the same
frequency distribution.

    I would expect that only a small percentage of words
would fail in this algorithm.  This algorithm would be
usefull for a rough comparison.  Adding another algorithm
to check the cases that might cause this algorithm to fail
would be usefull. 

#include <stdio_h>

int freq_match(mask, test)
   char *mask;
   char *test;
{
   static int freq_count[256];  /* the frequency */
                                /* distribution  */

   int divergence;
   int i;

   /* freq_match("maskwork",""); */
   if (test[0] == '\0) {
      /* initialize the distribution array */
      for (i=0; i<256; i++)
         freq_count[i++] = 0;

      /* compute the distribution */
     for ( i=0; mask[i] != '\0'; i++)
         freq_count[mask[i]] += 1;

     /* return a zero for initialization */
     return 0;
   }

   /* freq_match("don't care","testword"); */
   else {
      /* subtract the freq. dist. of the test word */
      for (i=0; test[i] != '\0'; i++)
         freq_count[test[i]] -= 1;

      /* compute the divergence */
      for (divergence=0, i=0; i<256; i++)
         divergence += abs(freq_count[i]);

      /* this code is to reset the freq. dist.  */
      /* back to the settings for the mask word */
      for ( i=0; test[i] != '\0'; i++ )
         freq_count[test[i]] += 1;

      return divergence;
   }
}

void main()
{
   char mask[80], test[80];

   printf("Mask:");
   gets(mask);

   printf("Test:");
   gets(test);

   freq_match(mask,"");
   printf("The divergence is %d.\n",
      freq_match("",test));

}


HELLO, WORLD

   In a number of recent postings to alt.folklore.computers,
the answer to the question "What's the shortest 'Hello,
World' program for a specific language?  For those that
don't know, 'Hello, World' is the first program used in K&R's
book on C and is synonomus with the simplest program to
write as a beginner.  The whole scope of the program is to
print the string 'Hello, World'.

   Here is a summary of the results of the postings:
   
BASIC:
------
   10 PRINT "Hello, World"
   
FORTRAN IV:
-----------
       WRITE(6,100)
   100 FORMAT(13H Hello World!)
       ST OP
       EN
      *D
    
COBOL:
------
   IDENTIFICATION DIVISION.
     PROGRAM-ID. WORLD.
   ENVIRONMENT DIVISION.
   DATA DIVISION.
   PROCEDURE DIVISION.
   BOO.
        DISPLAY "Hello, world!" UPON CONSOLE.
	STOP RUN.

ADA:
----
   With STANDARD_IO;
   Use STANDARD_IO;
   Procedure HELLO_WORLD is
   begin
      Put("Hello world!");
   end HELLO_WORLD;

C:
--
   #include <stdio.h>
   main()
   {
     printf('Hello, World!");
   }

perl:
-----
   #/usr/local/bin/perl
   print "Hello, World!\n";

Postscript:
-----------
   100 100 moveto /Times-Roman findfont 24 scalefont
     (Hello, World!) show showpage

FORTH:
------
   ."Hello, World!"

Lisp:
-----
   (print "Hello, World!")
   
Smalltalk:
----------
   'Hello, World!' PrintN1 !

PILOT:
------
   10 T:Hello, World!
   
REXX:
-----
   SAY "Hello, World!"
   
Modula-2:
---------
   MODULE HelloWorld;
   IMPORT InOut;
   BEGIN
      WriteString ("Hello, World!");
      WriteLn;
   END HelloWorld.

Here is an interesting one in C.  I have not checked this
one to see if it runs under C68.

   #define _ int) putchar
    main() { int c = 0110
    ;_(c);c+=29;_(c);c+=7
     ;_(c);_(c);c+=3;_(c)
     ;c=0x20;_(c);c=0127
      ;_(c);c+=24;_(c);
       c+=0x3;_(c);c-=
        6;_(c);c-=010;
	 _(c);c=0x21;
	  _(c);_(10)
	   ;c+=66;}

Nokolisp:  (which had only symbols - no character strings)
---------
   (compress (append (explode 'Hello) (cons 32 (explode
       'world.))))
   

NATURAL LANGUAGE

   A while back I asked for some help in porting a program
listed in the C Users Journal.  Emiliano Barbaini of Italy
volunteered to help.  I sent him the code and in return he
sent me back the changes he made to get the program to run
on the QL and on an BULL DPX/2.

   I took the changes, made them, and then compiled the
program.  It compiled fine, but I did not notice any changes
to the output (still did not work).  Emiliano used C68 v
3.05 where as I used v 3.03 (Hey, I know 3.14c is the
latest, just shows how often I use C68).  This might have
made the difference.

   I want not too sure about presenting the code here, but
Emiliano was able to get it running on a QL.  So, I'm sure
it's just something that I am doing (or not doing).  Give it
a try and see how it works for you.

   There were some follow on articles in the C Users Journal
that expanded this Natural Language program.  If you are
interested, I can photocopy the articles for you.  The
source code for recent issues of the CUJ are available on
the Internet.
 
/* natural_c */
#include <stdlib_h>
#include <stdio_h>
#include <string_h>
#include <ctype_h>

#define  ING   73        /* Restriction for ING word */

void initialize(void);
void reset_sentence(void);
void get_record(char *);
char *extract_word(void);
int  match_record(char *, int);
char *extract_root(void);
void check_underlying(void);
int  check_type(char *,int);
void check_subject(void);
void check_action(void);
void check_place(void);
void make_response(void);
void make_answer(int);
void get_verb_ing(void);
int  match_verb_ing(void);

FILE *infile;
char dic_record[80];
int  sentence;
int  word_ct;
char word_array[10][15];
char root_array[10][15];
char prime_types[10][11];
char phrases[10][11];
char type_array[10][5][11];
char subjects[20][15];
char actions[20][15];
char places[20][31];
char response[80];


void main()
{
    char  *cur_word;
    char  in_sentence[80];

    initialize();
    if ((infile = fopen("diction", "r+")) == NULL) {
        printf ("\nError opening dictionary\n");
        exit(0);
    }
    printf("\nSentence: ");

    while(gets(in_sentence)) {
        if (in_sentence[0] == '\0') break;
        reset_sentence();

        cur_word = strtok(in_sentence, " ");
        while(cur_word != NULL) {
            get_record(cur_word);
            cur_word = strtok(NULL, " ");
            if (++word_ct > 9) break;
        }

        check_underlying();

        check_subject();
        check_action();
        check_place();

        make_response();
        printf("Response: %s\n\nSentence: ", response);

        if (++sentence > 19) break;
    }   /*  end while  */

    fclose(infile);
    return;
}

/*****************************************************/
/* Initialize variables (subjects, actions and       */
/* places arrays contain entries for 20 sentences).  */
/*****************************************************/
void initialize()
{
    int i;
    for (i=0; i<20; i++) {
        subjects[i][0]    = '\0';
        actions[i][0]     = '\0';
        places[i][0]      = '\0';
    }
    sentence              = 0;
    return;
}

/*****************************************************/
/* These variables are initialized for each new      */
/* input sentence (each of the 10 word entries for   */
/* the input sentence has 5 type_array entries).     */
/*****************************************************/
void reset_sentence()
{
    int i,j;
    word_ct                     = 0;
    for (i=0; i<10; i++) {
        word_array[i][0]        = '\0';
        root_array[i][0]        = '\0';
        prime_types[i][0]       = '\0';
        phrases[i][0]           = '\0';
        for (j=0; j<5; j++)
            type_array[i][j][0] = '\0';
    }
    return;
}

/*****************************************************/
/* Get all the records from the dictionary. If the   */
/* passed word is not in the dictionary, then the    */
/* word could be a name.                             */
/*****************************************************/
void get_record(char *pass_word)
{
    int types = 0;
    rewind (infile);
    fgets(dic_record, 80, infile);
    while (! feof(infile)) {
        if (match_record(pass_word, types) == 0)
            types++;
        fgets(dic_record, 80, infile);

/***** Deleted Line 
       puts(dic_record);
*********/

    }
    if (types == 0) {

/*** Old Line ***/
        if (isupper( (int) pass_word[0]))
/****         ***/

        if (isupper(pass_word[0]) != 0)
            strcpy(type_array[word_ct][types], "NAME");
        else
            strcpy(type_array[word_ct][types],
                   "NOTFOUND");
    }
    strcpy(word_array[word_ct], pass_word);
    return;
}

/*****************************************************/
/* Compare the passed word with the word in the      */
/* current dictionary record. If they are the same,  */
/* then extract the type (NOUN, VERB, etc.). If the  */
/* type is a VERB, then also extract the root and    */
/* and copy it to the root array.                    */
/*****************************************************/
int  match_record(char *pass_word, int types)
{
    int i, j;
    char *root;
    char *dic_word;
    dic_word = extract_word();
    /* Check if passed word equals dictionary word   */
    if (strcmpi(pass_word, dic_word) != 0) return(1);

    /* Word found, get the type                      */
    for (i=14,j=0; i<20; i++) {
       if (isspace(dic_record[i])) break;
       type_array[word_ct][types][j++] = dic_record[i];
    }
    /* Trim the type                                 */
    type_array[word_ct][types][j] = '\0';

    if (strcmp(type_array[word_ct][types],
                "VERB") == 0) {
        root = extract_root();
        strcpy(root_array[word_ct], root);
    }

    return(0);
}

/*****************************************************/
/* Extract the word from the dictionary. The word is */
/* 14 characters in length and starts in column 1.   */
/*****************************************************/
char *extract_word()
{
    int i, j;
    char dic_word[15];
    for (i=0,j=0; i<14; i++) {
        if (isspace(dic_record[i])) break;
        dic_word[j++] = dic_record[i];
    }
    /* Trim the dictionary word                      */
    dic_word[j] = '\0';
    return(dic_word);
}

/*****************************************************/
/* Extract the root from the dictionary. It          */
/* identifies a group of similar words (the root for */
/* run, ran, runs and running is run). It is 14      */
/* characters in length and starts in column 35.     */
/*****************************************************/
char *extract_root()
{
    int i, j;
    char root[15];
    for (i=34,j=0; i<48; i++) {
        if (isspace(dic_record[i])) break;
        root[j++] = dic_record[i];
    }
    /* Trim the root                                 */
    root[j] = '\0';
    return(root);
}

/*****************************************************/
/* Determine if the input sentence contains a known, */
/* underlying structure. If it does, then assign the */
/* correct types and phrases for the words.          */
/*****************************************************/
void check_underlying()
{
    int i;

    /* Structure WH-AUX-NAME-VERB                    */
    i = 0;
    if ( (check_type("WH",     i) == 0) &&
         (check_type("AUX",  i+1) == 0) &&
         (check_type("NAME", i+2) == 0) &&
         (check_type("VERB", i+3) == 0) ) {
        strcpy(prime_types[i],   "WH");
        strcpy(prime_types[i+1], "AUX");
        strcpy(prime_types[i+2], "NAME");
        strcpy(prime_types[i+3], "VERB");
        strcpy(phrases[i],   "WHQUESTION");
        strcpy(phrases[i+1], "VERBPHRASE");
        strcpy(phrases[i+2], "NOUNPHRASE");
        strcpy(phrases[i+3], "VERBPHRASE");
        return;
    }


    /* Structure WH-AUX-VERB-NAME    *** NEW ****    */
    i = 0;
    if ( (check_type("WH",     i) == 0) &&
         (check_type("AUX",  i+1) == 0) &&
         (check_type("VERB", i+2) == 0) &&
         (check_type("NAME", i+3) == 0) ) {
        strcpy(prime_types[i],   "WH");
        strcpy(prime_types[i+1], "AUX");
        strcpy(prime_types[i+2], "VERB");
        strcpy(prime_types[i+3], "NAME");
        strcpy(phrases[i],   "WHQUESTION");
        strcpy(phrases[i+1], "VERBPHRASE");
        strcpy(phrases[i+2], "VERBPHRASE");
        strcpy(phrases[i+3], "NOUNPHRASE");
        return;
    }


    /* Structure NAME-AUX-VERB-PREP-DET-NOUN         */
    if ( (check_type("NAME",   i) == 0) &&
         (check_type("AUX",  i+1) == 0) &&
         (check_type("VERB", i+2) == 0) &&
         (check_type("PREP", i+3) == 0) &&
         (check_type("DET",  i+4) == 0) &&
         (check_type("NOUN", i+5) == 0) ) {
        strcpy(prime_types[i],   "NAME");
        strcpy(prime_types[i+1], "AUX");
        strcpy(prime_types[i+2], "VERB");
        strcpy(prime_types[i+3], "PREP");
        strcpy(prime_types[i+4], "DET");
        strcpy(prime_types[i+5], "NOUN");
        strcpy(phrases[i],   "NOUNPHRASE");
        strcpy(phrases[i+1], "VERBPHRASE");
        strcpy(phrases[i+2], "VERBPHRASE");
        strcpy(phrases[i+3], "PREPPHRASE");
        strcpy(phrases[i+4], "PREPPHRASE");
        strcpy(phrases[i+5], "PREPPHRASE");
        return;
    }

    return;
}

/*****************************************************/
/* Compare the passed type with all the types for    */
/* this word in the type_array. If the type is       */
/* found, then return 0. The pass_number parameter   */
/* identifies the word in the input sentence.        */
/*****************************************************/
int check_type(char *pass_type, int pass_number)
{
    int i;
    for (i=0; type_array[pass_number][i][0]; i++) {
        if (strcmp(type_array[pass_number][i],
                    pass_type) == 0)
            /*  Passed type is found in array        */
            return(0);
    }
    /*  Passed type is not found in array            */
    return(1);
}

/*****************************************************/
/* If the correct type is "NAME", then the word      */
/* refers to a subject so copy the word to the       */
/* subjects array.                                   */
/*****************************************************/
void check_subject()
{
    int i;
    for (i=0; i<word_ct; i++) {
        if (strcmp(prime_types[i], "NAME") == 0) {
            strcpy(subjects[sentence], word_array[i]);
            break;
        }
    }
    return;
}

/*****************************************************/
/* If the correct type is "VERB", then the word      */
/* refers to an action so copy the word's root from  */
/* the root array to the actions array.              */
/*****************************************************/
void check_action()
{
    int i;
    for (i=0; i<word_ct; i++) {
        if (strcmp(prime_types[i], "VERB") == 0) {
            strcpy(actions[sentence], root_array[i]);
            break;
        }
    }
    return;
}

/*****************************************************/
/* If the phrase is a "PREPPHRASE", then all the     */
/* words in the phrase refer to a place. Concatenate */
/* these words to the places array.                  */
/*****************************************************/
void check_place()
{
    int i;
    for (i=0; i<word_ct; i++) {
        if (strcmp(phrases[i], "PREPPHRASE") == 0) {
            strcat(places[sentence], " ");
            strcat(places[sentence], word_array[i]);
        }
    }
    return;
}

/*****************************************************/
/* Determine the kind of response to generate. If    */
/* the input sentence is a where-question and the    */
/* subject and action is found in a previous array   */
/* entry, then the response can state the location   */
/* of where the subject and action occured.          */
/*****************************************************/
void make_response()
{
    int i;

    /* Last input sentence is not a where-question   */
    if (strcmpi(word_array[0],"where") != 0) {
        strcpy(response, "Ok");
        return;
    }

    /* Last input sentence is a where-question       */
    for (i=sentence-1; i >= 0; i--) {
        if ( (strcmp(subjects[i],
                      subjects[sentence]) == 0) &&
             (strcmp(actions[i],
                      actions[sentence])  == 0) &&
             (strlen(places[i])           != 0) ) {
            make_answer(i);
            return;
        }
    }

    /* Not enough information in actions and         */
    /* subjects arrays.                              */
    strcpy(response, "I don't know");
    return;
}

/*****************************************************/
/* Generate a response that states the location of   */
/* where the subject and action occured.             */
/*****************************************************/
void make_answer(int prev_sentence)
{
    strcpy(response, subjects[prev_sentence]);
    strcat(response, " ");
    strcat(response, "was ");
    get_verb_ing();
    strcat(response, places[prev_sentence]);
    return;
}

/*****************************************************/
/* Retrieve the ING version of the word from the     */
/* dictionary (the ING version of run is running).   */
/*****************************************************/
void get_verb_ing()
{
    rewind (infile);
    fgets(dic_record, 80, infile);
    while (! feof(infile)) {
        if (match_verb_ing() == 0) break;
        fgets(dic_record, 80, infile);
    }
    return;
}

/*****************************************************/
/* If the root in the current dictionary record      */
/* matches the root in the actions array, and the    */
/* current dictionary record has an ING restriction, */
/* then extract the dictionary word and return 0.    */
/*****************************************************/
int  match_verb_ing()
{
    int i;
    char *root;
    char *dic_word;

    root = extract_root();
    if (strcmp(actions[sentence],root) == 0) {
        /* Root found, look for ING restriction      */
        for (i=24; i<33; i++) {
            if (isspace(dic_record[i])) break;
            if (dic_record[i] == ING) {
                dic_word = extract_word();
                strcat(response, dic_word);
                return(0);
            }
        }
    }
    return(1);
}


Dictionary file : diction

a             DET
the           DET
house         NOUN
street        NOUN
store         NOUN
jump          NOUN
go            VERB                go
goes          VERB                go
going         VERB      I         go
went          VERB                go
run           VERB                run
runs          VERB                run
running       VERB      I         run
ran           VERB                run
walk          VERB                walk
walks         VERB                walk
walking       VERB      I         walk
walked        VERB                walk
jump          VERB                jump
jumps         VERB                jump
jumping       VERB      I         jump
jumped        VERB                jump
is            AUX
was           AUX
to            PREP
in            PREP
on            PREP
where         WH


