[comp.sources.mac] Number Talk Source

tedj@hpcilzb.HP.COM (Ted Johnson) (10/19/88)

[Number Talk Source]

This is a program called Number Talk.  If you type in a number
(e.g., "-234.35"), Number Talk will spell it ("Negative Two
Hundred Thirty-Four Point Three Five") and will also speak it,
provided you have a MacinTalk driver in your System Folder.

Have fun!

-Ted C. Johnson

[Moderator's Note:  The binary for this program has been posted to 
 comp.binaries.mac.]

---
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#
#	number.c
#
# This archive created: Tue Oct 18 09:35:23 1988
# By:	Roger L. Long (bytebug@dhw68k.cts.com)
#
export PATH; PATH=/bin:$PATH
echo shar: extracting "'number.c'" '(8687 characters)'
if test -f 'number.c'
then
	echo shar: will not over-write existing file "'number.c'"
else
sed 's/^X//' << \SHAR_EOF > 'number.c'
X/*
X*Program name:  Number Talk
X*
X*Author:  The guts of this program come from an unknown Unix machine.  
X*		  There was no documentation, so the author is unknown.  I did
X*		  a quick port to the Mac (and Lightspeed C), and added the 
X*		  speech feature.  Ted C. Johnson, August 10, 1988.  As far as
X*		  I know, the program is Public Domain (FreeWare).  Have fun!
X*
X*Compilation instructions:  use Lightspeed C, v. 2.15.  This program does
X*							NOT use a resource file.  It was tested on a
X*							Mac SE HD20, running System/Finder 4.1/5.5.
X*
X*Summary:  This program translates a number (e.g., -123.454) into English 
X*		   ("Negative One Hundred Twenty-Three Point Four Five Four"),
X*		   and speaks it.  Number Talk handles positive and negative 
X*		   integers and floating point numbers, but does not accept commas.
X*
X*		   Number Talk will translate the number to either a cardinal 
X*		   number (e.g., 4 becomes "Four") or an ordinal number (e.g.,
X*		   4 becomes "Fourth".
X*  
X*		   Type "o" for ordinal mode.
X*		   Type "c" for cardinal mode.
X*		   Type "q" to quit.
X* 		   Type a number, hit carriage return, and Number Talk will
X*		   spell it and then speak it.
X*
X*NOTE:  You must have the MacinTalk driver in your System Folder to
X*		run this program!
X*/
X
X#include <stdio.h>
X#include <strings.h>
X#include <MacTypes.h>
X#include <MacinTalk.h>
X
X
X
X#define	    TRUE	1
X#define	    FALSE	0
X
Xchar words[BUFSIZ];
XStr255 s;
X
XSpeechHandle theSpeech;
XHandle theText;
X
X
X
Xmain(argc,argv)
Xint  argc;
Xchar *argv[];
X{
Xdouble n;			    /* Holds number for pass to ftow */
Xchar *numb;			    /* Pointer to return from number */
Xint thend;			    /* Flag for ordinal numbers */
Xextern char *ftow();
Xextern double atof();
Xint quit = FALSE;
X
X	thend = FALSE;
X	
X	SpeechOn("",&theSpeech);
X	theText = NewHandle(0);
X	
X	printf("Welcome to \"Number Talk\"\n");
X	say("Welcome to Number Talk", FALSE);
X	say("(a public domain program ported by Ted C Johnson)", TRUE);
X	printf("\n");
X	say("Enter any number, and I will spell it and speak it", TRUE);
X	say("Please don't use commas!", TRUE);
X	say("Type c for cardinal mode", TRUE);
X	say("Type o for ordinal mode", TRUE);
X	say("Type q to quit", TRUE);
X	printf("\n");
X	say("Here we go", TRUE);
X	
X	printf("\n\n");
X	
X	for (;!quit;) {
X		say("Enter a number",FALSE);
X		printf("Enter a number (or a command)-->");
X		gets(s);
X		
X		switch(s[0]) {
X			case 'q':
X				quit = TRUE;
X				break;
X				
X			case 'o':
X				thend = TRUE;/*ordinal numbers wanted*/
X				break;
X				
X			case 'c':
X				thend = FALSE;
X				break;
X				
X			default:
X	  			n = atof(s);			/* Get a float for ftow */
X    			numb = ftow(n,thend);		/* Generate the words */
X    			
X    			if (n < 0) {
X    				say("negative", FALSE);
X    			}
X    			
X    			say(s, FALSE);
X    			say("in words is", FALSE);
X    			printf("%s\n",numb);		/* Print it */
X    			say(numb, FALSE);
X
X    			break;
X   	 	}
X   	}/*for*/
X   	
X   	SpeechOff(theSpeech);
X}
X
X
X
Xsay(s, printit)
XStr255 s;
Xint printit;
X{
X	if (printit) {
X		printf("%s\n", s);
X	}
X	Reader(theSpeech, s, (long)strlen(s), theText);
X	MacinTalk(theSpeech, theText);
X}
X
X/* Table for name of each three-digit group */
Xstatic char *units[] =			
X    {					
X    " Trillion",
X    " Billion",
X    " Million",
X    " Thousand",
X    "\0\0\0\0\0"
X    };
X    
X    
X    
X/* Names of numbers that are unique */
Xstatic char *numbers[] =		
X    {
X    "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight",
X    "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen",
X    "Sixteen", "Seventeen", "Eighteen", "Nineteen", "Twenty",
X    "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"
X    };
X    
X    
X    
X/* Ordinal number coding table */
Xstatic char *ord[] =			
X    {
X    "+th","First","Second","Third","+th","Fifth","+th","+th","+h",
X    "Ninth","+th","+th","+th","+th","+th","+th","+th","+th","+th","+th"
X    };
X
X
X
X/*
X*Routine to convert the number to words
X*Entry:
X*x = double precision number to convert
X*ordflag = do you want ordinal numbers? (0 = no, !0 = yes)
X*Returns:
X*A character pointer to the words
X*/
Xchar *ftow(x,ordflag)
Xdouble x;
Xint ordflag;
X{
Xregister int i;				/* Loop counter */
Xchar numb[25];				/* Holds printf'ed number */
Xchar hold[30];				/* Gets 3 digit grouping */
Xint negflag = FALSE;			/* Is number negative? */
Xint n;					/* All around number */
X
Xextern void thcon();			/* Convert each thousands group */
X
X	words[0] = '\0';			/* Initialize for strcat */
X	
X	/* More than one quadrillion */
X	if (x >= 1e15) {
X    	strcpy(words,"Overflow");
X    	return(words);
X  	}
X  	
X	/* Just tack "Negative" on, */
X	if (x < 0) {
X    	strcpy(words,"Negative ");
X    	x = -x;				/*  and convert the positive */
X    	negflag = TRUE;			/* But let rest of the routine know */
X    }
X
X	sprintf(numb,"%23.7f",x);		/* Put number into printable form */
X	for (i = 0; numb[i] == ' '; ++i) {	/* Tack on leading zeros */
X    	numb[i] = '0';
X	}
X	
X	
X	if (x < 1) {
X    	strcat(words,numbers[0]);		/* Add a "Zero" if it is less than 1 */
X    }
X	else {
X    	for (i = 0; i < 5; i++)	{	/* Loop through each thousands group */
X			sscanf(&numb[i*3],"%3d",&n);	/* Get number less than one thousand */
X			thcon(hold,n);			/* Convert it */
X			
X			/* If it isn't zero */
X			if (*hold != '\0') {
X	    		if (words[0] != '\0') {	/* If it isn't the first word */
X					if (!negflag)		/* If the last word wasn't "Negative" */
X		    		strcat(words,", "); /* Add a comma */
X					strcat(words,hold);	/* Then add the words */
X					negflag = FALSE;	/* No more "Negative" */
X				}
X	    	else {
X				strcpy(words,hold);	/* Use copy if it is the first word */
X			}
X	    	strcat(words,units[i]);	/* And tack on the "Million", etc */
X	    	}
X	}
X    }
Xsscanf(&numb[16],"%d",&n);		/* Check for stuff after the decimal */
Xif (n != 0)				/* If there is stuff */
X    {
X    strcat(words," Point");		/* Add decimal point */
X    for (i = 16; numb[i] != '\0'; i++)	/* Get to end of number */
X	;
X    i--;
X    while (numb[i] == '0')		/* Strip off trailing zeros */
X	i--;
X    numb[i+1] = '\0';
X    for (i= 16; numb[i] != '\0'; i++)	/* Pull off one digit at a time */
X	{
X	strcat(words," ");		/* Add the space */
X	n = numb[i] - '0';		/* And convert to decimal */
X	strcat(words,numbers[n]);	/* Tack on the right number */
X	}
X    }
Xelse					/* If there ain't no decimals */
X    {
X    if (ordflag)			/* And you want ordinals */
X	{
X	for (i = 0; words[i] != '\0'; ++i)  /* Get to end of words */
X	    ;
X	i--;
X	if (words[i] == 'y')		/* If it is a "Twenty", etc */
X	    {
X	    words[i] = '\0';		/* Change the "y" to "ieth" */
X	    strcat(words,"ieth");
X	    }
X	else				/* Search for beginning of last word */
X	    {
X	    while(words[i-1] != ' ' && words[i-1] != '-' && i > 0)
X		i--;
X	    for (n = 0; n < 20; n++)	/* Find out what the last word is */
X		if (strcmp(&words[i],numbers[n]) == 0)
X		    break;
X	    if (n > 19)			/* If we can't figure out what it is */
X		strcat(words,"th");	/*  just add a "th" */
X	    else
X		{
X		if (ord[n][0] == '+')	/* Plus = cat the rest of the string */
X		    strcat(words,&ord[n][1]);
X		else			/* Otherwise make an entire replace */
X		    {
X		    words[i] = '\0';
X		    strcat(words,ord[n]);
X		    }
X		}
X	    }
X	}
X    }
X
Xfor (n = 0; ; n = i)			/* Divide words up into < 80 byte */
X    {					/*  sections separated by a newline */
X    for (i = n; i < n+80; ++i)
X	if (words[i] == '\0')
X	    return(words);
X    while (words[i] != ' ')
X	--i;
X    words[i] = '\n';
X    }
X}
X
X
X
X/*
X*Routine to convert a number less than one thousand into words.  Basic
X*routine, because all groups of three digits similar.
X*Entry:
X*buf = a place to put the converted number
X*z = the number to convert
X*Return:
X*Nothing
X*/
X
Xvoid thcon(buf,z)
Xregister int z;
Xchar buf[];
X{
Xregister int d;			    /* The divided down number */
Xregister int spflag = FALSE;	    /* Do we need a space catted? */
X
X	buf[0] = '\0';	/* Initialize */
X	
X 	/* Converting zero is easy */
X	if (z != 0) {
X    	d = z / 100; /* Find out if we need and hundreds */
X    	
X    	if (d != 0){
X			strcat(buf,numbers[d]);	 /* Tack them on */
X			strcat(buf," Hundred");
X			spflag = TRUE;		    /* Need a space afterward */
X		}
X
X   		 z %= 100;			    /* The < 100 stuff */
X   		  /* Is there any? */
X    	if (z != 0)	 {
X			if (spflag)	{	    /* Need a separator */
X	    		strcat(buf," ");
X	    	}
X	    	/* Simple "Forty-Four" type construct */
X			if (z > 19)	 {
X	   	 		d = z / 10 + 18;	    /* Get the "Forty" part into d */
X	    		z %= 10;		    /* Get the "Four" part into z */
X	    		strcat(buf,numbers[d]); /* Cat the stuff */
X	    		if (z != 0) {
X				strcat(buf,"-");
X				strcat(buf,numbers[z]);
X				}
X	    	}
X			else {			    /* Just use "One" to "Nineteen" */
X	    		strcat(buf,numbers[z]);
X	    	}
X		}
X    }
X	return;
X}
SHAR_EOF
if test 8687 -ne "`wc -c < 'number.c'`"
then
	echo shar: error transmitting "'number.c'" '(should have been 8687 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
---