langtest.c


/*

  LANGTEST.C
  =======
  (c) Paul Griffiths 1999
  Email: paulgriffiths@mcmail.com

  Simple foreign language phrase tester

*/


#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include "list.h"


/*  Forward function declarations  */

int GetInput(int argc, char *argv[]);
int Rndm(int lower, int higher);
int Getline(char *buffer, int maxlen, FILE *fp);

/*  Global variable to hold the names of the two languages  */

char langs[2][MAX_LINE];


/*  main()  */

int main(int argc, char *argv[]) {

    int   nPhrases, q, lang1, lang2;
    char *phrase[2], buffer[MAX_LINE] = {0};


    /*  Seed random number generator  */

    srand( (unsigned) time(NULL) );

    
    /*  Get input from files, and store number of phrases  */

    nPhrases = GetInput(argc, argv);


    /*  Loop until the user types 'q' or 'Q'  */

    while ( buffer[0] != 'q' && buffer[0] != 'Q' ) {


	/*  Pick a random phrase  */

	q = Rndm(0, nPhrases-1);

	
	/*  Decide which language to test  */

	lang1 = Rndm(0, 1);
	lang2 = lang1 ? 0 : 1;


	/*  Get the relevant phrase, and ask question  */

	GetItem(q, &phrase[0], &phrase[1]);
	printf("The %s is %s\n", langs[lang1], phrase[lang1]);
	printf("Enter the %s: ", langs[lang2]);
	fgets(buffer, MAX_LINE-1, stdin);
	printf("The %s is %s\n\n", langs[lang2], phrase[lang2]);
    }

    
    /*  Clean up nicely and exit  */

    FreeList();
    return EXIT_SUCCESS;
}


/*  Gets phrases from specified data files  */

int GetInput(int argc, char *argv[]) {

    int   i, a = 1, length = 0;
    FILE *fp;
    char  buffer1[MAX_LINE], buffer2[MAX_LINE];


    /*  Loop through input files specified
	as command line arguments           */

    while ( argv[a] ) {
	if ( ! (fp = fopen(argv[a], "r")) ) {
	    printf("LANGTEST: Error opening %s for reading...", argv[a]);
	    printf("skipping\n");
	    continue;
	}

	/*  Determine the two languages used from
	    first two lines in the data file       */

	for ( i = 0; i < 2; i++ ) {
	    if (Getline(buffer1, sizeof(langs[0])/sizeof(langs[0][0])-1, fp)) {
		Trim(buffer1);
		strcpy(langs[i], buffer1);
	    }
	    else {
		printf("LANGTEST: Error determining languages.\n");
		exit(EXIT_FAILURE);
	    }
	}
	
	
	/*  Get pairs of input lines until we have
	    no more pairs left in this file         */
	    
	while ( Getline(buffer1, MAX_LINE-1, fp) ) {
	    if ( Getline(buffer2, MAX_LINE-1, fp) ) {
		Trim(buffer1);
		Trim(buffer2);

		
		/*  Add both phrases to list  */

		if ( AppendItem(buffer1, buffer2) ) {
		    printf("LANGTEST: Failed to allocate item.\n");
		    exit(EXIT_FAILURE);
		}
		++length;
	    }
	    else
		break;
	}

	++a;
	fclose(fp);
    }
    return length;
}


/*  Wrapper function to get a random number -
    rand() % n; is not very random, especially
    with the lower order bits.                  */

int Rndm(int lower, int higher) {

    int range = higher - lower + 1;

    return lower + (int)((double) rand() / ((double)RAND_MAX + 1) * range);
}


/*  Wrapper around fgets(), so we can handle comments
    (lines that start with '#') and blank lines.       */

int Getline(char *buffer, int maxlen, FILE *fp) {

    char *temp;


    /*  Loop until we have an acceptable line  */

    do {

	/*  Exit on error  */
	
	if ( !fgets(buffer, maxlen, fp) )
	    return 0;
	temp = buffer;

	
	/*  Find first non-whitespace character  */

	while ( *temp && isspace(*temp) )
	    ++temp;


	/*  Continue loop if we have a comment or a blank line  */

    } while ( *temp == 0 || *temp == '#' );

    return 1;
}
	



list.h


/*

  LIST.H
  ======
  (c) Paul Griffiths, 1999
  Email: mail@paulgriffiths.net

  Interface to linked list operations

*/


#ifndef PG_LIST_H
#define PG_LIST_H


/*  Global macros  */

#define MAX_LINE              (100)


/*  Function declarations  */

int AppendItem(char *foreign, char *english);
int FreeList();
int GetItem(int index, char **phrase1, char **phrase2);
int Trim(char *buffer);


#endif  /*  PG_LIST_H  */



list.c


/*

  LIST.C
  ======
  (c) Paul Griffiths, 1999
  Email: mail@paulgriffiths.net

  Implementation of linked list functions

*/


#include <stdlib.h>
#include <ctype.h>
#include "list.h"


/*  Our linked list node structure  */

typedef struct node {
    char *phrase[2];
    struct node *next;
} node;


/*  Pointers to start and end of node  */

node *startnode = NULL, *endnode = NULL;


/*  Adds an node onto the end of the list  */

int AppendItem(char *phrase1, char *phrase2) {

    node *tempnode;


    /*  Allocate memory for a new node  */

    if ( !endnode ) {

	/*  If endnode is NULL, we have an empty 
	    list, so create the first element now.  */

	if ( ! (startnode = malloc(sizeof(node))) )
	    return 1;
	endnode = startnode;
	startnode->next = NULL;
    }
    else {

	/*  Otherwise, make a new node and make
	    our current last node point to it.   */

	if ( ! (tempnode = malloc(sizeof(node))) )
	    return 1;
	endnode->next = tempnode;
	endnode = tempnode;
    }

    
    /*  Initialise node members  */

    endnode->next = NULL;

    if ( ! (endnode->phrase[0] = malloc(strlen(phrase1)+1)) )
	return 1;
    strcpy(endnode->phrase[0], phrase1);

    if ( ! (endnode->phrase[1] = malloc(strlen(phrase2)+1)) )
	return 1;
    strcpy(endnode->phrase[1], phrase2);


    return 0;
}


/*  Retrieves from the list the item at the specified index  */

int GetItem(int index, char **phrase1, char **phrase2) {

    int n = 0;
    node *tempnode = startnode;


    /*  Cycle through list to specified index  */

    while ( index > n++ )
	tempnode = tempnode->next;


    /*  Update function arguments to point to the data  */

    *phrase1 = tempnode->phrase[0];
    *phrase2 = tempnode->phrase[1];

    return 0;
}


/*  Free's all the malloc'ed memory in our list  */

int FreeList() {

    node *tempnode = startnode;

    while ( tempnode ) {
	free(tempnode->phrase[0]);
	free(tempnode->phrase[1]);
	startnode = tempnode->next;
	free(tempnode);
	tempnode = startnode;
    }

    return 0;
}


/*  Remove trailing whitespace and '\n's from a string  */

int Trim(char *buffer) {

    int n = strlen(buffer) - 1;

    while ( !isalnum(buffer[n]) )
	buffer[n--] = '\0';

    return 0;
}



testdata.dat


# testdata.dat
#
# Sample LANGTEST data file
#
# These lines are comments

# The first two uncommented lines signify the
# two languages we will be translating. They
# should be in the same order as the pairs of
# phrases which follow

Japanese
English


# Now that we have specified our languages, 
# we can list the pairs of phrases in the
# order specified above. Blank lines are
# ignored by LANGTEST, so we can use them or
# omit them as we please.

O-hayo gozaimasu
Good morning

Konnichi wa
Hello

Konban wa
Good evening

O-yasumi nasai
Good night

Hajimemashite
Pleased to meet you

Sayonara
Goodbye

O-namae wa nan to osshaimasu ka
What is your name?

Tanaka to moshimasu
My name is Tanaka

Sumimasen
Excuse me



Please send all comments, suggestions, bug reports etc to mail@paulgriffiths.net