#include <stdio.h>
#include <ctype.h>

#ifdef LIBSPEAK
#ifdef getc
#undef getc
#endif getc
#define getc(FP)	hacked_getc(FP)
#endif LIBSPEAK

#define MAX_LENGTH 128

#ifdef LIBSPEAK
FILE *In_file;
FILE *Out_file;
#else
static FILE *In_file;
static FILE *Out_file;
#endif LIBSPEAK

static int Char, Char1, Char2, Char3;

#ifndef LIBSPEAK
/*
** main(argc, argv)
**	int argc;
**	char *argv[];
**
**	This is the main program.  It takes up to two file names (input
**	and output)  and translates the input file to phoneme codes
**	(see ENGLISH.C) on the output file.
*/
main(argc, argv)
	int argc;
	char *argv[];
	{
	if (argc > 3)
		{
		fputs("Usage: PHONEME [infile [outfile]]\n", stderr);
		exit();
		}

	if (argc == 1)
		{
		fputs("Enter english text:\n", stderr);
		}

	if (argc > 1)
		{
		In_file = fopen(argv[1], "r");
		if (In_file == 0)
			{
			fputs("Error: Cannot open input file.\n", stderr);
			fputs("Usage: PHONEME [infile [outfile]]\n", stderr);
			exit();
			}
		}
	else
		In_file = stdin;

	if (argc > 2)
		{
		Out_file = fopen(argv[2], "w");
		if (Out_file == 0)
			{
			fputs("Error: Cannot create output file.\n", stderr);
			fputs("Usage: PHONEME [infile [outfile]]\n", stderr);
			exit();
			}
		}
	else
		Out_file = stdout;

	xlate_file();
	}
#endif LIBSPEAK

outstring(string)
	char *string;
	{
#ifdef LIBSPEAK
	phoneme_str_to_audio(string);
#else
	while (*string != '\0')
		outchar(*string++);
#endif
	}

#ifndef LIBSPEAK
outchar(chr)
	int chr;
	{
	fputc(chr,Out_file);
	}
#endif LIBSPEAK


int makeupper(character)
	int character;
	{
	if (islower(character))
		return toupper(character);
	else
		return character;
	}

new_char()
	{
	/*
	If the cache is full of newline, time to prime the look-ahead
	again.  If an EOF is found, fill the remainder of the queue with
	EOF's.
	*/
	if (Char == '\n'  && Char1 == '\n' && Char2 == '\n' && Char3 == '\n')
		{	/* prime the pump again */
		Char = getc(In_file);
		if (Char == EOF)
			{
			Char1 = EOF;
			Char2 = EOF;
			Char3 = EOF;
			return Char;
			}
		if (Char == '\n')
			return Char;

		Char1 = getc(In_file);
		if (Char1 == EOF)
			{
			Char2 = EOF;
			Char3 = EOF;
			return Char;
			}
		if (Char1 == '\n')
			return Char;

		Char2 = getc(In_file);
		if (Char2 == EOF)
			{
			Char3 = EOF;
			return Char;
			}
		if (Char2 == '\n')
			return Char;

		Char3 = getc(In_file);
		}
	else
		{
		/*
		Buffer not full of newline, shuffle the characters and
		either get a new one or propagate a newline or EOF.
		*/
		Char = Char1;
		Char1 = Char2;
		Char2 = Char3;
		if (Char3 != '\n' && Char3 != EOF)
			Char3 = getc(In_file);
		}
	return Char;
	}

/*
** xlate_file()
**
**	This is the input file translator.  It sets up the first character
**	and uses it to determine what kind of text follows.
*/
xlate_file()
	{
	/* Prime the queue */
	Char = '\n';
	Char1 = '\n';
	Char2 = '\n';
	Char3 = '\n';
	new_char();	/* Fill Char, Char1, Char2 and Char3 */

	while (Char != EOF)	/* All of the words in the file */
		{
		if (isdigit(Char))
			have_number();
		else
		if (isalpha(Char) || Char == '\'')
			have_letter();
		else
		if (Char == '$' && isdigit(Char1))
			have_dollars();
		else
			have_special();
		}
	}

have_dollars()
	{
	long int value;

	value = 0L;
	for (new_char() ; isdigit(Char) || Char == ',' ; new_char())
		{
		if (Char != ',')
			value = 10 * value + (Char-'0');
		}

	say_cardinal(value);	/* Say number of whole dollars */

	/* Found a character that is a non-digit and non-comma */

	/* Check for no decimal or no cents digits */
	if (Char != '.' || !isdigit(Char1))
		{
		if (value == 1L)
			outstring("dAAlER ");
		else
			outstring("dAAlAArz ");
		return;
		}

	/* We have '.' followed by a digit */

	new_char();	/* Skip the period */

	/* If it is ".dd " say as " DOLLARS AND n CENTS " */
	if (isdigit(Char1) && !isdigit(Char2))
		{
		if (value == 1L)
			outstring("dAAlER ");
		else
			outstring("dAAlAArz ");
		if (Char == '0' && Char1 == '0')
			{
			new_char();	/* Skip tens digit */
			new_char();	/* Skip units digit */
			return;
			}

		outstring("AAnd ");
		value = (Char-'0')*10 + Char1-'0';
		say_cardinal(value);

		if (value == 1L)
			outstring("sEHnt ");
		else
			outstring("sEHnts ");
		new_char();	/* Used Char (tens digit) */
		new_char();	/* Used Char1 (units digit) */
		return;
		}

	/* Otherwise say as "n POINT ddd DOLLARS " */

	outstring("pOYnt ");
	for ( ; isdigit(Char) ; new_char())
		{
		say_ascii(Char);
		}

	outstring("dAAlAArz ");

	return;
	}

have_special()
	{
#ifndef LIBSPEAK
	if (Char == '\n')
		outchar('\n');
	else
#endif
	if (!isspace(Char))
		say_ascii(Char);

	new_char();
	return;
	}


have_number()
	{
	long int value;
	int lastdigit;

	value = Char - '0';
	lastdigit = Char;

	for (new_char() ; isdigit(Char) ; new_char())
		{
		value = 10 * value + (Char-'0');
		lastdigit = Char;
		}

	/* Recognize ordinals based on last digit of number */
	switch (lastdigit)
		{
	case '1':	/* ST */
		if (makeupper(Char) == 'S' && makeupper(Char1) == 'T' &&
		    !isalpha(Char2) && !isdigit(Char2))
			{
			say_ordinal(value);
			new_char();	/* Used Char */
			new_char();	/* Used Char1 */
			return;
			}
		break;

	case '2':	/* ND */
		if (makeupper(Char) == 'N' && makeupper(Char1) == 'D' &&
		    !isalpha(Char2) && !isdigit(Char2))
			{
			say_ordinal(value);
			new_char();	/* Used Char */
			new_char();	/* Used Char1 */
			return;
			}
		break;

	case '3':	/* RD */
		if (makeupper(Char) == 'R' && makeupper(Char1) == 'D' &&
		    !isalpha(Char2) && !isdigit(Char2))
			{
			say_ordinal(value);
			new_char();	/* Used Char */
			new_char();	/* Used Char1 */
			return;
			}
		break;

	case '0':	/* TH */
	case '4':	/* TH */
	case '5':	/* TH */
	case '6':	/* TH */
	case '7':	/* TH */
	case '8':	/* TH */
	case '9':	/* TH */
		if (makeupper(Char) == 'T' && makeupper(Char1) == 'H' &&
		    !isalpha(Char2) && !isdigit(Char2))
			{
			say_ordinal(value);
			new_char();	/* Used Char */
			new_char();	/* Used Char1 */
			return;
			}
		break;
		}

	say_cardinal(value);

	/* Recognize decimal points */
	if (Char == '.' && isdigit(Char1))
		{
		outstring("pOYnt ");
		for (new_char() ; isdigit(Char) ; new_char())
			{
			say_ascii(Char);
			}
		}

	/* Spell out trailing abbreviations */
	if (isalpha(Char))
		{
		while (isalpha(Char))
			{
			say_ascii(Char);
			new_char();
			}
		}

	return;
	}


have_letter()
	{
	char buff[MAX_LENGTH];
	int count;

	count = 0;
	buff[count++] = ' ';	/* Required initial blank */

	buff[count++] = makeupper(Char);

	for (new_char() ; isalpha(Char) || Char == '\'' ; new_char())
		{
		buff[count++] = makeupper(Char);
		if (count > MAX_LENGTH-2)
			{
			buff[count++] = ' ';
			buff[count++] = '\0';
			xlate_word(buff);
			count = 1;
			}
		}

	buff[count++] = ' ';	/* Required terminating blank */
	buff[count++] = '\0';

	/* Check for AAANNN type abbreviations */
	if (isdigit(Char))
		{
		spell_word(buff);
		return;
		}
	else
	if (strlen(buff) == 3)	 /* one character, two spaces */
		say_ascii(buff[1]);
	else
	if (Char == '.')		/* Possible abbreviation */
		abbrev(buff);
	else
		xlate_word(buff);

	if (Char == '-' && isalpha(Char1))
		new_char();	/* Skip hyphens */

	}

/* Handle abbreviations.  Text in buff was followed by '.' */
abbrev(buff)
	char buff[];
	{
	if (strcmp(buff, " DR ") == 0)
		{
		xlate_word(" DOCTOR ");
		new_char();
		}
	else
	if (strcmp(buff, " MR ") == 0)
		{
		xlate_word(" MISTER ");
		new_char();
		}
	else
	if (strcmp(buff, " MRS ") == 0)
		{
		xlate_word(" MISSUS ");
		new_char();
		}
	else
	if (strcmp(buff, " PHD ") == 0)
		{
		spell_word(" PHD ");
		new_char();
		}
	else
		xlate_word(buff);
	}
