+++ /dev/null
-/*
- * Copyright (c) 1988 Regents of the University of California.
- * Copyright (c) 1992 Joe Dellinger, University of Hawaii at Manoa
- * Copyright (c) 2005 Eric S. Raymond.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-/*
- * A morse code practice utility. (Contains those characters that can appear
- * on the FCC ham license exam.)
- *
- * Running "morse" without arguments or input gives self-doc.
- *
- * It doesn't keep PERFECT time, but it seems reasonably close
- * for reasonable word speeds on my slow SUN IPC!
- *
- * This code is (ridiculously) heavily modified from morse.c from the Reno UNIX
- * distribution. I (Joe) also used slightly modified versions of a subroutine
- * from Richard Ottolini at Unocal for Sun workstation tone generation.
- * Scott Seligman at Stanford added support for other sorts of devices and
- * made several other changes. John Shalamskas helped test and made comments.
- *
- * For a more detailed history, see the HISTORY file
- */
-
-/*
- * Useful for seeing what the interleaved reading and writing loops are
- * really up to.
- *
- * #define DEBUG
- *
- * If you want to be overwhelmed with information about the probabilities
- * of each letter being chosen.
- *
- * #define DEBUGG
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <ctype.h>
-#include <signal.h>
-#include "beep.h"
-
-#define FREQUENCY 800.0
-#define FREQUENCY2 602.0
-#define VOLUME 0.5
-#define ERROR_VOLUME 0.5
-#define WORDS_PER_MINUTE 20.0
-#define MAX_BEHINDNESS 0
-#define ERROR_FREQUENCY 2000.0
-
-static int whichfrequ = 0;
-static float frequency1 = FREQUENCY;
-static float frequency2 = FREQUENCY2;
-static float frequency;
-static float volume = VOLUME;
-static float dot_time;
-static float dash_time;
-static float intra_char_time;
-static float inter_char_time;
-static float inter_word_time;
-static float catchup_time;
-static int showletters = 0;
-static int showmorse = 0;
-static int wordsbefore = 0;
-static int wordsafter = 0;
-static int fancyending = 1;
-static int noticebad = 0;
-static int testing = 0;
-static int showtesting = 0;
-static int keepquiet = 0;
-static int dynamicspeed = 0;
-static int charbychar = 0;
-static int international = 0;
-static int allprosigns = 0;
-static int allpunctuation = 1;
-static int tryagaincount = 1;
-static float words_per_minute = WORDS_PER_MINUTE;
-static float error_frequency = ERROR_FREQUENCY;
-static float error_volume = ERROR_VOLUME;
-static float fwords_per_minute;
-static int randomletters = 0;
-#define LETMESEE 2
-static int typeaway = 0;
-
-static int totalhitcount = 0;
-static int totalmisscount = 0;
-static int helpmeflag = 0;
-
-#define MAXWORDLEN 20
-#define TESTBUFSZ ((MAXWORDLEN+1)*10)
-static int wordlen = MAXWORDLEN;
-static int wordcount = -1;
-static time_t starttime;
-static int timeout = -1;
-static int testpointer = -1;
-static int testlength = 0;
-static int behindness = 0;
-static int max_behindness = MAX_BEHINDNESS;
-static char teststring[TESTBUFSZ];
-static int yourpointer = -1;
-static int yourlength = 0;
-static char yourstring[TESTBUFSZ];
-static int linepos = 0;
-
-/*
- * How many times can a given character not be asked before
- * kicking up the probability of asking that one by one randomfactor unit.
- */
-#define RIPECOUNT 64
-
-#define TWOFIFTYSIX 256
-static char *(code[TWOFIFTYSIX]);
-static int errorlog[TWOFIFTYSIX];
-static int randomfactor[TWOFIFTYSIX];
-static int randomripe[TWOFIFTYSIX];
-static int testterminal (void);
-static int randomletter (void);
-
-/*
- * Pointers to termcap/terminfo "smso/so" (enter_standout_mode) and "rmso/se"
- * (exit_standout_mode) terminal string capabilities (initialized respectively
- * with ANSI sequences "\E[7m" to turn the inverse video mode on and "\E[0m"
- * to turn all video attributes off).
- */
-static const char *enter_standout_mode = "\033[7m";
-static const char *exit_standout_mode = "\033[0m";
-
-/*
- * Value of (Wrong - Right), which, if exceeded, will cause the program
- * to start prompting you. Above MAX_ERROR_THRESHOLD it will never prompt.
- * Don't let the user bank too much credit for past correct answers;
- * limit it by min(ERROR_FLOOR, error_threshold).
- */
-#define MAX_ERROR_THRESHOLD 1000
-#define ERROR_FLOOR -3
-static int error_threshold = MAX_ERROR_THRESHOLD;
-static int error_floor = ERROR_FLOOR;
-
-/*
- * How many characters behind before it decides you're having
- * trouble keeping up.
- */
-#define BEHIND 1
-#define WAYBEHIND 3
-#define TOOFARBEHIND 6
-/*
- * If SLOWPOKE or more wpm ticks go by, then it decides you are having lots
- * of trouble remembering this character, and need to be asked it more
- * often.
- */
-#define SLOWPOKE 10
-/* You aren't slow -- you left and came back! */
-#define SLOWPOKEMAX (50 * SLOWPOKE)
-/*
- * If FASTPOKE or less wpm ticks go by, then it decides you are good at this
- * character, and need to be asked it less often.
- */
-#define FASTPOKE 4
-
-/*
- * These control how quickly the dynamicspeed option acts when you are
- * fast or slow. Easier to slow down than speed up!
- */
-#define ERRORSLOWER 1.04
-#define ALOTSLOWER 1.15
-#define ALITTLESLOWER 1.02
-#define ALITTLEFASTER 1.02
-
-/*
- * How many inter_char_time's to give you to answer after the end of
- * a word before considering that you are not keeping up.
- * Maximum of 2.3, minimum of 0.
- * The bigger the value, the easier it is to kick in the "automatic
- * speedup" when using the "-d" option. The maximum means you have (almost)
- * right up to the beginning of the next word to answer and still have it
- * count as keeping up.
- */
-#define SPORTING_RATIO 1.5
-
-/*
- * The bigger, the more evenly things start out.
- * (Must be at least 2)
- */
-#define RANDOMBASELEVEL 7
-/*
- * RANDOMINCWORSE scales how badly you are punished for being wrong
- * or taking too long. RANDOMINCBETTER scales how you are rewarded for
- * answering quickly or being right.
- */
-#define RANDOMINCWORSE 6
-#define RANDOMINCBETTER 7
-#define RANDOMMAX (30 * RANDOMBASELEVEL)
-/*
- * The average length of a random word (chosen using exponential distribution).
- * After implementing this I'm not so sure an exponential distribution
- * actually models the distribution of real word lengths in English very well.
- * It's not too bad, though, and the words themselves are all garbage anyway,
- * so what the heck.
- */
-#define RANDWORDLEN 3.5
-
-/* Put in a newline instead of a space when past this column */
-#define LINELENGTH 78
-
-/* An EOF without the EOF (%) sound */
-#define SILENTEOF -2
-/* Toggle tone frequency on control-G within input file */
-#define FREQU_TOGGLE ((int)'\007')
-
-/*
- * If you want the morse code to come out synchronized with the printing
- * of dots and dashes with the -m option, then define this. The problem
- * is that then the morse code sounds ratty on slower CPU's.
- * John Shalamskas (KJ9U) suggested turning the precise morse-code printing
- * synching off because he didn't like the resulting code quality!
- */
-#undef FLUSHCODE
-
-static void new_words_per_minute (void);
-static void dowords (int c);
-static void morse (int c);
-static void show (char *s);
-static void testaddchar (char c);
-static void youraddchar (char c);
-static void pollyou (void);
-static void tone (float hertz, float duration, float amplitude);
-static void toneflush (void);
-static void openterminal (void);
-static int readterminal (char **string);
-static void closeterminal (void);
-static void report (void);
-
-static void die (), suspend ();
-static void cleanup ();
-
-int
-main (int argc, char **argv)
-{
-extern char *optarg;
-extern int optind;
-int ch;
-char *p;
-int ii, jj;
-int firsttime, notdoneyet;
-int yourchar;
-float randexp, randnum;
-extern time_t time ();
-
- if (argc == 1 && isatty (fileno (stdin)))
- {
-/*
- * SELF DOC
- */
- printf ("Usage:\n");
- printf ("morse [options] < text_file\n");
- printf ("morse [options] words words words\n");
- printf ("morse [options] -r\n");
- printf ("morse [options] -i\n");
- printf ("Options:\n");
- printf ("-i Play what you type.\n");
- printf ("-I Like -i but don't turn off keyboard echoing.\n");
- printf ("-r Generate random text. Starts out slanted towards easy\n");
- printf (" letters, then slants towards ones you get wrong.\n");
- printf ("-n NUM (default %d means random length)\n", MAXWORDLEN);
- printf (" Make words (groups) NUM characters long.\n");
- printf (" Valid values are between %d and %d.\n", 1, MAXWORDLEN);
- printf ("-R NUM (default 0 means unlimited)\n");
- printf (" Set the total time (in minutes) to generate text.\n");
- printf ("-N NUM (default 0 means unlimited)\n");
- printf (" Set the total number of words (groups) to generate.\n");
- printf ("-C \'STRING\' (default all available characters)\n");
- printf (" Select characters to send from this STRING only.\n");
- printf ("-w words_per_minute (default %g)\n-f frequency_in_hertz (default %g)\n-v volume (zero to one, rather nonlinear, default %g)\n",
- WORDS_PER_MINUTE, FREQUENCY, VOLUME);
- printf ("-g alternate_frequency (default %g)\n (toggles via control-G in input FILE at a word break)\n", FREQUENCY2);
- printf ("-F Farnsworth_character_words_per_minute\n");
- printf ("-e leave off the <SK> sound at the end\n");
- printf ("-c complain about illegal characters instead of just ignoring them\n");
- printf ("-b print each word before doing it\n");
- printf ("-a print each word after doing it\n");
- printf ("-l print each letter just before doing it\n");
- printf ("-m print morse dots and dashes as they sound\n");
-#ifdef FLUSHCODE
- printf (" (this printing-intensive option slows the wpm down!)\n");
-#endif
- printf ("-t Type along with the morse, but don't see what\n");
- printf (" you're typing (unless you make a mistake).\n");
- printf (" You are allowed to get ahead as much as you want.\n");
- printf (" If you get too far behind it will stop and resync with you.\n");
- printf (" You can force it to resync at the next word end by hitting control-H.\n");
- printf (" Hit ESC to see how you are doing, control-D to end.\n");
- printf (" (The rightmost space in the printout marks where the average is.\n");
- printf (" Farther left spaces separate off blocks of letters that are\n");
- printf (" about twice as probable as the average to occur, three times, etc.)\n");
- printf ("-T Like -t but see your characters (after they are played).\n");
- printf ("-s Stop after each character and make sure you get it right. (implies -t)\n");
- printf ("-q Quietly resyncs with your input (after you make a mistake).\n");
- printf ("-p NUM (default 0)\n");
- printf (" Make you get it right NUM times, for penance. (implies -s)\n");
- printf (" (Yes, NUM = 0 means you can sin all you want.)\n");
- printf ("-E NUM (default %d)\n", MAX_ERROR_THRESHOLD);
- printf (" If your count of wrong answers minus right answers for a given character\n");
- printf (" exceeds this, the program will start prompting you.\n");
- printf (" If %d or above, it will never prompt. (implies -t)\n", MAX_ERROR_THRESHOLD);
- printf ("-M NUM (default %d)\n", MAX_BEHINDNESS);
- printf (" If you get more than this number of characters behind, pause until you\n");
- printf (" do your next letter. (1 behind is normal, 0 behind means never pause.)\n");
- printf (" (implies -t)\n");
- printf ("-d Dynamically speed up or slow down depending on how you are doing.\n");
- printf (" (if also -s, then -d _only speeds up_!)\n");
- printf ("-A Add ISO 8850-1 (Latin-1) signs to test set.\n");
- printf ("-B Add uncommon punctuation to test set.\n");
- printf ("-S Add uncommon prosigns to test set.\n");
- printf ("-X Set error volume. Defaults to %g.\n", ERROR_VOLUME);
- printf (" Error volume 0 means use console speaker.\n");
- printf ("-x Set frequency of error tone, default 2000.0Hz\n");
- exit (0);
- }
-
- for (ii = 0; ii < TWOFIFTYSIX; ii++)
- code[ii] = NULL;
-
-/* Load in the morse code code */
- code[(int) 'a'] = ".-";
- code[(int) 'b'] = "-...";
- code[(int) 'c'] = "-.-.";
- code[(int) 'd'] = "-..";
- code[(int) 'e'] = ".";
- code[(int) 'f'] = "..-.";
- code[(int) 'g'] = "--.";
- code[(int) 'h'] = "....";
- code[(int) 'i'] = "..";
- code[(int) 'j'] = ".---";
- code[(int) 'k'] = "-.-";
- code[(int) 'l'] = ".-..";
- code[(int) 'm'] = "--";
- code[(int) 'n'] = "-.";
- code[(int) 'o'] = "---";
- code[(int) 'p'] = ".--.";
- code[(int) 'q'] = "--.-";
- code[(int) 'r'] = ".-.";
- code[(int) 's'] = "...";
- code[(int) 't'] = "-";
- code[(int) 'u'] = "..-";
- code[(int) 'v'] = "...-";
- code[(int) 'w'] = ".--";
- code[(int) 'x'] = "-..-";
- code[(int) 'y'] = "-.--";
- code[(int) 'z'] = "--..";
-
- code[(int) '1'] = ".----";
- code[(int) '2'] = "..---";
- code[(int) '3'] = "...--";
- code[(int) '4'] = "....-";
- code[(int) '5'] = ".....";
- code[(int) '6'] = "-....";
- code[(int) '7'] = "--...";
- code[(int) '8'] = "---..";
- code[(int) '9'] = "----.";
- code[(int) '0'] = "-----";
-
- /* Punctuation marks */
- code[(int) '.'] = ".-.-.-";
- code[(int) ','] = "--..--";
- code[(int) '?'] = "..--..";
- code[(int) '/'] = "-..-.";
- code[(int) '-'] = "-....-";
-
- if (allpunctuation) {
- /* Not so commonly used punctuation marks */
- code[(int) ')'] = "-.--.-";
- code[(int) '\"'] = ".-..-.";
- code[(int) '_'] = "..--.-";
- code[(int) '\''] = ".----.";
- code[(int) ':'] = "---...";
- code[(int) ';'] = "-.-.-.";
- code[(int) '$'] = "...-..-";
- code[(int) '!'] = "-.-.--";
- }
-
- /* Commonly used procedure signs ("prosigns") */
- code[(int) '+'] = ".-.-."; /* <AR> end of message */
- code[(int) '*'] = ".-..."; /* <AS> wait, stand-by */
- code[(int) '='] = "-...-"; /* <BT> (double dash) pause, break for text */
- code[(int) '('] = "-.--."; /* <KN> over-specified station only */
- code[(int) '%'] = "...-.-"; /* <SK> end of contact, known also as <VA> */
-
- if (allprosigns) {
- /* Not so commonly used procedure signs ("prosigns") */
- code[(int) '^'] = ".-.-"; /* <AA> new line, the same as :a, ae */
- code[(int) '#'] = "-...-.-";/* <BK> invite receiving station to transmit */
- code[(int) '&'] = "-.-.-"; /* <KA> attention */
- code[(int) '@'] = "...-."; /* <SN> understood */
- }
-
- if (international) {
- /* Not so commonly used international extensions (ISO 8859-1) */
- code[(int) ((unsigned char)'\344')] = ".-.-"; /* :a (also for ae), the same as <AA> */
- if (code[(int) '^'] != NULL) code[(int) '^'] = NULL;
- code[(int) ((unsigned char)'\340')] = ".--.-"; /* `a, oa (danish a with ring over it) */
- code[(int) ((unsigned char)'\347')] = "----"; /* ch (bar-ch) */
- code[(int) ((unsigned char)'\360')] = "..--."; /* -d (eth, overstrike d with -) */
- code[(int) ((unsigned char)'\350')] = "..-.."; /* `e */
- code[(int) ((unsigned char)'\361')] = "--.--"; /* ~n */
- code[(int) ((unsigned char)'\366')] = "---."; /* :o (also for oe) */
- code[(int) ((unsigned char)'\374')] = "..--"; /* :u (also for ue) */
- code[(int) ((unsigned char)'\376')] = ".--.."; /* ]p (thorn, overstrike ] with p) */
- code[(int) ((unsigned char)'\247')] = ".-.-.."; /* paragraph */
- }
-
- for (ii = 0; ii < TWOFIFTYSIX; ii++)
- {
- /* Everything starts equally fresh */
- randomripe[ii] = 0;
- /* Start out assuming you know how everything sounds */
- errorlog[ii] = 0;
-
- if (code[ii] == NULL)
- {
- /* Ensures these will never be chosen */
- randomfactor[ii] = 0;
- }
- else
- {
- /* Start out favoring easy ones */
- randomfactor[ii] = RANDOMBASELEVEL - strlen (code[ii]);
- if (randomfactor[ii] < 1)
- randomfactor[ii] = 1;
- }
- }
-
- words_per_minute = WORDS_PER_MINUTE;
- fwords_per_minute = -1.;
-
- /* DGHJKLMOPQUVWYZhjkouyz are still available */
- while ((ch = getopt (argc, argv, "ABC:E:F:IM:N:R:STXabcdef:g:ilmn:p:qrstv:w:x:")) != EOF)
- switch ((char) ch)
- {
- case 'A':
- international = 1;
- break;
- case 'B':
- allpunctuation = 1;
- break;
- case 'C':
- for (ii = 0; ii < strlen(optarg); ii++)
- {
- *(optarg+ii) = tolower(*(optarg+ii));
- }
- for (ii = 0; ii < TWOFIFTYSIX; ii++)
- {
- if (code[ii] != NULL)
- {
- if (strchr(optarg, ii) != NULL)
- {
- randomfactor[ii] = RANDOMBASELEVEL - 1;
- } else {
- code[ii] = NULL;
- randomfactor[ii] = 0;
- }
- }
- }
- break;
- case 'E':
- testing = 1;
- sscanf (optarg, "%d", &error_threshold);
- if (error_threshold < error_floor)
- error_floor = error_threshold;
- break;
- case 'F':
- sscanf (optarg, "%f", &fwords_per_minute);
- break;
- case 'I':
- typeaway = LETMESEE;
- break;
- case 'M':
- testing = 1;
- sscanf (optarg, "%d", &max_behindness);
- if (max_behindness < 1)
- max_behindness = 0;
- break;
- case 'N':
- sscanf (optarg, "%d", &wordcount);
- if (wordcount < 1) wordcount = -1;
- break;
- case 'R':
- sscanf (optarg, "%d", &timeout);
- timeout *= 60;
- if (timeout < 1) timeout = -1;
- break;
- case 'S':
- allprosigns = 1;
- break;
- case 'T':
- testing = 1;
- showtesting = 1;
- break;
- case 'X':
- sscanf (optarg, "%f", &error_volume);
- break;
- case 'a':
- wordsafter = 1;
- break;
- case 'b':
- wordsbefore = 1;
- break;
- case 'c':
- noticebad = 1;
- break;
- case 'd':
- dynamicspeed = 1;
- break;
- case 'e':
- fancyending = 0;
- break;
- case 'f':
- sscanf (optarg, "%f", &frequency1);
- break;
- case 'g':
- sscanf (optarg, "%f", &frequency2);
- break;
- case 'i':
- typeaway = 1;
- break;
- case 'l':
- showletters = 1;
- break;
- case 'm':
- showmorse = 1;
- break;
- case 'n':
- sscanf (optarg, "%d", &wordlen);
- if (wordlen < 1) wordlen = 1;
- if (wordlen > MAXWORDLEN) wordlen = MAXWORDLEN;
- break;
- case 'p':
- charbychar = 1;
- testing = 1;
- sscanf (optarg, "%d", &tryagaincount);
- break;
- case 'q':
- keepquiet = 1;
- break;
- case 'r':
- randomletters = 1;
- break;
- case 's':
- charbychar = 1;
- testing = 1;
- break;
- case 't':
- testing = 1;
- break;
- case 'v':
- sscanf (optarg, "%f", &volume);
- if (volume < 0.)
- volume = 0.;
- if (volume > 1.)
- volume = 1.;
- break;
- case 'w':
- sscanf (optarg, "%f", &words_per_minute);
- break;
- case 'x':
- sscanf (optarg, "%f", &error_frequency);
- break;
- default:
- fprintf (stderr, "Type \"morse\" without arguments to get self-doc!\n");
- exit (1);
- break;
- }
- argc -= optind;
- argv += optind;
-
- if (fwords_per_minute <= 0.)
- fwords_per_minute = words_per_minute;
- new_words_per_minute ();
-
- frequency = frequency1;
-
- if (BeepInit () != 0)
- {
- fprintf (stderr, "Can't access speaker.\n");
- exit (1);
- }
-
- time(&starttime);
-
- sigset (SIGINT, die);
- sigset (SIGTERM, die);
- sigset (SIGQUIT, die);
- sigset (SIGTSTP, suspend);
-
- if (testing || typeaway)
- {
- openterminal ();
- }
-
-/*
- * Do .25 seconds of silence initially to give the workstation time to
- * get settled after the stress of starting this program and opening
- * up everything.
- */
- tone (frequency, .25, 0.);
- toneflush ();
-
- if (typeaway)
- {
- testing = 0;
- showtesting = 0;
- charbychar = 0;
- wordsbefore = 0;
- wordsafter = 0;
- randomletters = 0;
-
- notdoneyet = 1;
-
- while (notdoneyet)
- {
- pollyou ();
-
- for (jj = 0; jj < yourlength; jj++)
- {
- yourchar = yourstring[(yourpointer - yourlength + 1 + jj + TESTBUFSZ) % TESTBUFSZ];
-
- /* Control-D: finished */
- if (yourchar == (int) '\004')
- {
- toneflush ();
- notdoneyet = 0;
- break;
- }
-
- if (isspace (yourchar))
- {
- if (showletters)
- {
- toneflush ();
- printf ("%c", yourchar);
- fflush (stdout);
- }
-
- tone (frequency, inter_word_time, 0.);
-
- continue;
- }
-
- morse (yourchar);
- }
- yourlength -= jj;
-
- if (timeout > 0)
- if (difftime(time(NULL),starttime) > (double)timeout)
- break;
- }
- }
- else if (randomletters)
- {
- srand48(time (NULL));
- randexp = 1. / (1. - 1. / (float) (RANDWORDLEN));
- while (1)
- {
- if ((wordcount == 0) || timeout == 0) break;
-
- dowords (randomletter ());
-
- if (wordlen == MAXWORDLEN)
- {
- /* Knock a few bits off the top so we're sure it won't overflow */
- /* Shift a few bits because the lower bits stink */
- /* Add in the time so it doesn't repeat from run to run */
- randnum = (float) (
- (((lrand48() >> 9) + (long) (time (NULL))) >> 4)
- & 0x00FFFFFF);
- randnum = randnum - randexp * (int) (randnum / randexp);
- if ((randnum >= 1.) && (linepos != 0)) dowords ((int) ' ');
- }
- }
- }
- else
- {
- if (*argv)
- {
- firsttime = 1;
-
- do
- {
- if (!firsttime)
- {
- dowords ((int) ' ');
- }
- else
- firsttime = 0;
-
- if ((wordcount == 0) || timeout == 0) break;
-
- for (p = *argv; *p; ++p)
- {
- dowords ((int) *p);
- if ((wordcount == 0) || timeout == 0) break;
- }
- if ((wordcount == 0) || timeout == 0) break;
-
- } while (*++argv);
- }
- else
- {
- while ((ch = getchar ()) != EOF)
- {
- dowords (ch);
- if ((wordcount == 0) || timeout == 0) break;
- }
- }
- }
-
- if (fancyending)
- dowords (EOF);
- else
- dowords (SILENTEOF);
-
- fflush (stdout);
-
- if (testing)
- {
- /*
- * WE'RE completely done, and YOU aren't! Force catch up. (Note if
- * charbychar = YES we won't get here, since we're always caught up
- * after each character as it comes out.)
- */
- while (testlength > 0)
- {
- tone (frequency, catchup_time, 0.);
- toneflush ();
- testterminal ();
- }
- }
-
- /* Just to be sure! */
- toneflush ();
-
- if (showmorse || wordsbefore || wordsafter || showletters || showtesting)
- printf ("\n");
- fflush (stdout);
-
- if (testing)
- report ();
-
-/* If you make any mistakes exit with a return code! */
- cleanup ();
- return (totalmisscount > 0);
-}
-
-static void
-new_words_per_minute ()
-{
-float wtick, ftick, tick;
-
- tick = 60. / (words_per_minute * 50);
-
- /*
- * In the limit as wpm goes past fwpm, Farnsworth becomes kosher PARIS
- */
- if (fwords_per_minute <= words_per_minute)
- ftick = 60. / (words_per_minute * 50);
- else
- ftick = 60. / (fwords_per_minute * 50);
-
- wtick = (50. * tick - 31. * ftick) / 19.;
-
- /*
- * This time is used when the computer is waiting on you to hit a key; it
- * is useful to scale the granularity with the real overrall words per
- * minute. This also serves as a measuring rod to see if you are
- * responding "fast enough". If you are too slow, then obviously you are
- * having trouble with that character, and should be given it more OFTEN.
- * Heh heh heh...
- */
- catchup_time = tick;
-
- /*
- * Things between characters and words go at the "remainder" speed,
- * whatever space you need to make the sped-up Farnsworth characters come
- * out with the correct overall words per minute.
- */
- inter_char_time = wtick * 3.;
- inter_word_time = wtick * 7.;
-
- /* Things within the character go at the Farnsworth speed */
- intra_char_time = ftick;
- dot_time = ftick;
- dash_time = ftick * 3.;
-}
-
-static int tryingagain = 0, slowpoke = 0;
-
-static void
-dowords (int c)
-{
-static int wordc = 0;
-static char word[MAXWORDLEN+1];
-char *wordp;
-int ii;
-int againcount = 0;
-int are_we_repeating;
-
-/*
- * Increment the line position counter.
- * If a line gets too long, just cut it off by inserting a new line.
- */
- if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE) linepos++;
- if (isspace (c) && ((linepos + wordlen) >= LINELENGTH)) c = '\n';
- if (c == '\n') linepos = 0;
-
- if (isspace (c) || c == EOF || c == SILENTEOF || c == FREQU_TOGGLE)
- {
- if (wordc > 0)
- {
- if (wordcount > 0) wordcount--;
- if (timeout > 0)
- if (difftime(time(NULL),starttime) > (double)timeout)
- timeout = 0;
-
- word[wordc] = '\0';
-
-/*
- * We have just read in a new complete word from the input, (hopefully)
- * during the time of an inter-word space minus an inter-char space.
- * Now let's go back and see what happened with the PREVIOUS word,
- * the one that we had just finished playing. Did the user keep
- * up with us?
- */
-#ifdef DEBUG
- fprintf (stderr, " [%d] ", behindness);
-#endif
- if (testing && dynamicspeed && !charbychar)
- {
- /*
- * (If charbychar then behindness is ALWAYS 0 at this
- * point...)
- */
- if (behindness == 0)
- {
- /* You're a speed demon! Speed up a bit, then! */
- words_per_minute *= ALITTLEFASTER;
- new_words_per_minute ();
- }
- else if (behindness > WAYBEHIND)
- {
- /* You're way behind! Slow way down. */
- words_per_minute /= ALOTSLOWER;
- new_words_per_minute ();
- }
- else if (behindness > BEHIND)
- {
- /* You're behind! Slow down a bit. */
- words_per_minute /= ALITTLESLOWER;
- new_words_per_minute ();
- }
- }
-
-
-/*
- * If the user was WAY too far behind stop and catch up with
- * the "new" word as the first one.
- */
- if (testing && (behindness > TOOFARBEHIND || helpmeflag))
- {
- if (helpmeflag)
- printf ("\nOK, let's restart.\n");
- else
- printf ("\nYou are too far behind! Let's restart.\n");
- fflush (stdout);
-
- toneflush ();
-
- /* Flush the keyboard buffer */
- pollyou ();
-
- /* Forget the past */
- helpmeflag = 0;
- behindness = 0;
- testlength = 0;
- yourlength = 0;
-
- /* Give the user a little rest. */
- sleep (2);
- printf ("\nWPM now %d\n", (int) (words_per_minute + .5));
- fflush (stdout);
- sleep (2);
- printf ("\nREADY?\n");
- fflush (stdout);
- sleep (1);
- printf ("\nSET\n");
- fflush (stdout);
- sleep (1);
- printf ("\nGO!\n");
- fflush (stdout);
- linepos = 0;
- }
-
-/*
- * Start treating the new word.
- */
- if (wordsbefore)
- {
- /* Try to keep your out-of-sync text from getting swirled in */
- if (showtesting)
- {
- printf ("\n");
- linepos = 0;
- }
-
- printf ("%s", word);
-
- if (showmorse || showletters || wordsafter || showtesting)
- {
- printf (" ");
- for (ii = 0; ii < 16 - (wordc + 2); ii++)
- {
- printf (" ");
- }
- }
-
- fflush (stdout);
- }
-
- if (testing && charbychar)
- {
- againcount = 0;
- }
-
- for (wordp = word; *wordp != '\0'; wordp++)
- {
- tryingagain = 0;
-
- tryagain:
- if (testing && !tryingagain && !showletters &&
- error_threshold < MAX_ERROR_THRESHOLD &&
- errorlog[(int) *wordp] > error_threshold)
- {
- toneflush ();
- /* Give them a quick hint */
- printf ("[%c]", *wordp);
- fflush (stdout);
-
- morse (*wordp);
-
- toneflush ();
- if (!showmorse)
- {
- /* Erase the hint */
- printf ("\b\b\b \b\b\b");
- fflush (stdout);
- }
- }
- else
- {
- morse (*wordp);
- }
-
- if (testing)
- {
- if (charbychar)
- {
- toneflush ();
- /* Force catchup */
- slowpoke = 0;
- while (behindness > 0)
- {
- if (testterminal () && tryagaincount > 0)
- {
- /*
- * OOPS! They got it WRONG! MAKE THEM TRY
- * AGAIN!
- */
- printf ("Try again.\n");
- linepos = 0;
- /*
- * Yeah I know gotos are inelegant but I
- * don't feel like figuring out the "elegant"
- * way to do this right now.
- */
- againcount = tryagaincount - 1;
- tryingagain = 1;
- goto tryagain;
- }
- else
- {
- /*
- * They got it right, or they didn't answer
- * yet.
- */
- if (behindness > 0)
- {
- /*
- * They are STILL thinking, the
- * slowpokes. Wait a bit before trying
- * again.
- */
- tone (frequency, catchup_time, 0.);
- /*
- * Keep track of how long they're taking
- * to answer!
- */
- if (slowpoke < SLOWPOKEMAX)
- slowpoke++;
- toneflush ();
- }
- else if (dynamicspeed && !slowpoke && !tryingagain)
- {
- /*
- * They got it right without errors the
- * first time and we didn't have to wait
- * for them! A speed demon! Speed up a
- * bit, then!
- */
- words_per_minute *= ALITTLEFASTER;
- new_words_per_minute ();
- }
- }
- }
-
- /* Insufficient penance? */
- if (againcount > 0)
- {
- againcount--;
- goto tryagain;
- }
- }
- else
- {
- testterminal ();
- /*
- * Stop if we get more than max_behindness ahead.
- * max_behindness == 0 means don't worry about them,
- * they can be as far behind as they want and we
- * won't stop!
- */
- if (max_behindness > 0)
- {
- are_we_repeating = 0;
- while (behindness >= max_behindness)
- {
-#ifdef DEBUG
- fprintf (stderr, " (%d) ", behindness);
-#endif
- if (are_we_repeating)
- {
- /*
- * Pause for a bit so we don't loop too
- * fast
- */
- tone (frequency, catchup_time, 0.);
- }
- else
- {
- are_we_repeating = 1;
- }
- /* Finish playing whatever we're playing */
- toneflush ();
- /* And give them another chance */
- testterminal ();
- }
- }
- }
- }
- }
-
- toneflush ();
- if (testing)
- testterminal ();
-
- if (wordsafter)
- {
- printf (" (%s)", word);
- }
-
- if (wordsbefore || wordsafter || showmorse)
- {
- if (!showmorse && !testing && linepos)
- {
- if (wordsafter)
- linepos += 3;
- else
- printf (" ");
- } else {
- printf ("\n");
- linepos = 0;
- }
- }
- else if (showletters || showtesting)
- {
- if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE)
- {
- if (showletters)
- printf ("%c", c);
-
- if (showtesting)
- testaddchar (c);
- }
- }
-
-/*
- * WHEW! FINISHED QUEUEING THE WORD FOR PLAYING!
- * Now finish up all the other sundry details...
- */
-
- /* Flush the output printing queue... */
- fflush (stdout);
- /*
- * Pause for a bit; this gives the user a sporting chance at
- * catching up with us.
- */
- tone (frequency, SPORTING_RATIO * inter_char_time, 0.);
- toneflush ();
- /* Start sounding an inter-word space */
- tone (frequency, inter_word_time - SPORTING_RATIO * inter_char_time, 0.);
-
- /* While that silence is playing check if the user has caught up. */
- if (testing)
- testterminal ();
-
- /* We finished this word; reset the word character count */
- wordc = 0;
- }
- else if (!(wordsbefore || wordsafter || showmorse)
- &&
- (showletters || showtesting))
- {
- if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE)
- {
- if (showletters)
- printf ("%c", c);
-
- if (showtesting)
- testaddchar (c);
- }
- }
-
- if (c == EOF)
- {
- morse (EOF);
- toneflush ();
- }
- else if (c == SILENTEOF)
- {
- toneflush ();
- }
- else if (c == FREQU_TOGGLE)
- {
- /* Switch to the other frequency */
- /* (Won't work from keyboard, only from a file.) */
- whichfrequ = 1 - whichfrequ;
- switch (whichfrequ)
- {
- case 1:
- frequency = frequency2;
- break;
- case 0:
- default:
- frequency = frequency1;
- break;
- }
- }
- }
- else
- {
- word[wordc++] = c;
- }
-
-/*
- * If a word gets too long, just cut it off by inserting a space.
- * Just call ourselves with the character we wish we'd gotten...
- */
- if (wordc == wordlen && !(isspace (c) || c == EOF || c == SILENTEOF || c == FREQU_TOGGLE))
- dowords ((int) ' ');
-}
-
-/*
- * Don't try to test the person DURING the call into morse!
- */
-void
-morse (int c)
-{
- if ((isalpha (c) && (code[tolower(c)] == NULL)) || ((code[(int) '%'] == NULL) && ((c == EOF) || (c == '\004'))))
- c = ' ';
-
- if (showletters)
- {
- if ((c == EOF) || (c == '\004'))
- {
- if (showmorse)
- {
- printf ("<SK>");
- } else {
- printf ("%%");
- }
- }
- else if (c == '.' && showmorse)
- printf ("<DOT>");
- else if (c == '-' && showmorse)
- printf ("<DASH>");
- else if (c == '+' && showmorse)
- printf ("<AR>");
- else if (c == '*' && showmorse)
- printf ("<AS>");
- else if (c == '=' && showmorse)
- printf ("<BT>");
- else if (c == '(' && showmorse)
- printf ("<KN>");
- else if (c == '%' && showmorse)
- printf ("<SK>");
-#ifdef ALLPROSIGNS
- else if (c == '^' && (code[(int) '^'] != NULL) && showmorse)
- printf ("<AA>");
- else if (c == '#' && showmorse)
- printf ("<BK>");
- else if (c == '&' && showmorse)
- printf ("<KA>");
- else if (c == '@' && showmorse)
- printf ("<SN>");
-#endif
- else
- printf ("%c", c);
-
- fflush (stdout);
- }
-
- if (isalpha (c))
- {
- if (testing)
- testaddchar (c - (isupper (c) ? 'A' : 'a') + 'a');
- show (code[c - (isupper (c) ? 'A' : 'a') + 'a']);
- }
- else if ((c == EOF) || (c == '\004'))
- {
- show (code[(int) '%']);
- }
- else if (code[c] != NULL)
- {
- if (testing)
- testaddchar (c);
- show (code[c]);
- }
- else
- {
- /* Oops! This letter is junk! */
-
- if (noticebad)
- {
- if (showletters)
- {
- fflush (stdout);
- }
-
- /* Simulate a stumble */
- tone (frequency, 2. * inter_word_time, 0.);
- toneflush ();
- }
-
- if (showletters)
- {
- /* Wipe out what we just printed */
- fflush (stdout);
- printf ("\b");
- printf (" ");
- printf ("\b");
- fflush (stdout);
- }
-
- if (noticebad)
- {
- if (showletters)
- {
- /* And replace it with an error message */
- printf ("<UNKNOWN_CHARACTER>");
- fflush (stdout);
- }
-
- /* Give the error call */
- show ("........");
-
- /* Regroup */
- tone (frequency, inter_word_time, 0.);
- }
- }
-
- if (showmorse)
- printf (" ");
- fflush (stdout);
- toneflush ();
- tone (frequency, inter_char_time - intra_char_time, 0.);
-}
-
-
-/*
- * Don't try to test the person WHILE doing dots and dashes!
- */
-static void
-show (char *s)
-{
-char c;
-
- while ((c = *s++) != '\0')
- {
- tone (frequency, intra_char_time, 0.);
-
-#ifdef FLUSHCODE
- if (showmorse)
- toneflush ();
-#endif
-
- switch (c)
- {
- case '.':
- tone (frequency, dot_time, volume);
- break;
- case '-':
- tone (frequency, dash_time, volume);
- break;
- }
-
- if (showmorse)
- {
- printf ("%c", c);
- fflush (stdout);
-#ifdef FLUSHCODE
- toneflush ();
-#endif
- }
- }
-}
-
-/*
- * This only gets passed valid characters: ones
- * that have a morse code associated with them
- * or ones for which isspace(c) is true.
- */
-static void
-testaddchar (char c)
-{
- testpointer = (testpointer + 1) % TESTBUFSZ;
- teststring[testpointer] = c;
-#ifdef DEBUG
- fprintf (stderr, " (%c,%d,%d) ", c, testlength, behindness);
-#endif
- testlength++;
- if (testlength > TESTBUFSZ)
- {
- fprintf (stderr, "\n\nInput buffer queue overflow! Make TESTBUFSZ bigger!\n");
- fprintf (stderr, "(Or don't fall so far behind)\n");
-
- die ();
- }
-
-/*
- * Since you are never asked to type spaces (you can type them if
- * you want, but they are ignored) spaces in the input file don't
- * count against your "behindness".
- */
- if (!isspace (c))
- behindness++;
-}
-
-static void
-youraddchar (char c)
-{
- yourpointer = (yourpointer + 1) % TESTBUFSZ;
- yourstring[yourpointer] = c;
-#ifdef DEBUG
- fprintf (stderr, " <%c,%d> ", c, yourlength);
-#endif
- yourlength++;
- if (yourlength > TESTBUFSZ)
- {
- fprintf (stderr, "\n\nKeyboard typeahead buffer queue overflow! Make TESTBUFSZ bigger!\n");
- fprintf (stderr, "(Or don't type so far ahead... how did you expect to get them right anyway?)\n");
-
- die ();
- }
-}
-
-static void
-pollyou (void)
-{
- int ii, num;
- char *string;
-
- num = readterminal (&string);
-
- for (ii = 0; ii < num; ii++)
- youraddchar (string[ii]);
-}
-
-static int
-testterminal (void)
-{
-int testinc, yourinc;
-int correctchar, yourchar, yourcharnocase;
-int errorcount;
-int resync;
-
- errorcount = 0;
-
-/*
- * There is nothing in the input file queue right now,
- * so we can't process any of your keystrokes.
- * Defer processing until we can catch up with YOU!
- */
- if (testlength == 0)
- return errorcount;
-
- /* We're ready for you; but are you ready for us? */
- pollyou ();
-
-/*
- * Process your entries and the input queue entries in parallel
- */
- if (yourlength > 0 && testlength > 0)
- {
- for (testinc = 0, yourinc = 0;
- testinc < testlength && yourinc < yourlength;
- testinc++, yourinc++)
- {
- correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ];
-
- /*
- * The latter half of this if shouldn't be necessary, but just in
- * case...
- */
- if (isspace (correctchar) || code[correctchar] == NULL)
- {
- if (showtesting)
- {
- printf ("%c", correctchar);
- fflush (stdout);
- }
-
- /* White space doesn't count for "behindness" */
- behindness++;
- /* The _other_ pointer wasn't used; don't increment it. */
- yourinc--;
-
- /* Short circuit the loop */
- continue;
- }
-
-
- yourchar = yourstring[(yourpointer - yourlength + 1 + yourinc + TESTBUFSZ) % TESTBUFSZ];
- if (isalpha (yourchar))
- yourcharnocase = yourchar - (isupper (yourchar) ? 'A' : 'a') + 'a';
- else
- yourcharnocase = yourchar;
-
- /* Did you type something rude? If so, just ignore it. */
- if (isspace (yourchar) || code[yourcharnocase] == NULL)
- {
- /* ESCAPE: dump status info */
- /* Control-D: dump status info and then bye bye */
- /* Control-H: force restart */
- if (yourchar == '\033' || yourchar == (int) '\004')
- {
- report ();
-
- if (yourchar == (int) '\004')
- die ();
- }
- else if (yourchar == '\b')
- {
- helpmeflag = 1;
- }
-
- /* The _other_ pointer wasn't used; don't increment it. */
- testinc--;
- /* Short circuit the loop */
- continue;
- }
-
- if (keepquiet > 1)
- {
- for (resync = testinc; resync < testlength; resync++)
- if ( yourcharnocase == teststring[(testpointer - testlength + 1 + resync + TESTBUFSZ) % TESTBUFSZ])
- {
- for (; testinc < resync; testinc++)
- {
- correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ];
- if (isspace (correctchar) || code[correctchar] == NULL)
- {
- if (showtesting) printf ("%c", correctchar);
- } else {
- printf("%s%c%s", enter_standout_mode, correctchar, exit_standout_mode);
- }
- }
- fflush (stdout);
- correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ];
- keepquiet = 1;
- break;
- }
- }
-
- if (yourcharnocase != correctchar)
- {
- errorcount++;
- totalmisscount++;
-
- /*
- * Record that you are having trouble with these.
- */
- errorlog[correctchar]++;
- if (code[yourcharnocase] != NULL &&
- errorlog[yourcharnocase] < MAX_ERROR_THRESHOLD)
- errorlog[yourcharnocase]++;
-
- if (keepquiet > 0)
- {
- printf("%s%c%s", enter_standout_mode, correctchar, exit_standout_mode);
- fflush (stdout);
- keepquiet++;
- } else {
- /*
- * Scold them for their mistake.
- */
- printf ("\n");
-
- if (error_volume) {
- /* Beep using the tone generator */
- toneflush ();
- tone (error_frequency, 0.1, error_volume);
- } else
- /* Beep using control-G */
- printf ("\007");
- printf ("%c (%s) for %c (%s)\n",
- yourchar, code[yourcharnocase],
- correctchar, code[correctchar]);
- fflush (stdout);
- linepos = 0;
- }
-
- if (charbychar)
- {
- /* Give them a bit of time to think about their error */
- tone (frequency, inter_word_time, 0.);
- toneflush ();
- }
- if (dynamicspeed && !charbychar)
- {
- /*
- * Slow down. Doesn't make sense to slow down for errors,
- * though, if you've got all the time you want to think
- * about each one.
- */
- words_per_minute /= ERRORSLOWER;
- new_words_per_minute ();
- }
- if (randomletters && !tryingagain)
- {
- /*
- * Ask ones that confused you more often!
- */
- if (code[yourcharnocase] != NULL)
- {
- randomfactor[yourcharnocase] += (3 * RANDOMINCWORSE / 2);
- if (randomfactor[yourcharnocase] > RANDOMMAX)
- randomfactor[yourcharnocase] = RANDOMMAX;
- }
-
- randomfactor[correctchar] += RANDOMINCWORSE * 2;
- if (randomfactor[correctchar] > RANDOMMAX)
- randomfactor[correctchar] = RANDOMMAX;
- }
- }
- else
- {
- /*
- * Record that you got this right.
- */
- if (!tryingagain)
- {
- totalhitcount++;
- if (errorlog[correctchar] > error_floor)
- errorlog[correctchar]--;
- }
-
- if (showtesting)
- {
- printf ("%c", yourchar);
- fflush (stdout);
- }
-
- if (randomletters && !tryingagain)
- {
- if (slowpoke == SLOWPOKEMAX)
- {
- printf ("\nNice to have you back again, I was getting bored!\n");
- linepos = 0;
- }
- else if (slowpoke >= SLOWPOKE * 3)
- {
- /*
- * Did you take too long thinking about it? If so,
- * you probably need to be asked this one more
- * often...
- */
- randomfactor[correctchar] += (3 * RANDOMINCWORSE / 2);
- if (randomfactor[correctchar] > RANDOMMAX)
- randomfactor[correctchar] = RANDOMMAX;
-
- /*
- * Hits this slow shouldn't count! You were obviously
- * just guessing! (But it doesn't count as an error
- * either.)
- */
- totalhitcount--;
- }
- else if (slowpoke > FASTPOKE)
- {
- randomfactor[correctchar] +=
- (slowpoke * RANDOMINCWORSE) / (2 * SLOWPOKE);
- if (randomfactor[correctchar] > RANDOMMAX)
- randomfactor[correctchar] = RANDOMMAX;
- }
- else if (slowpoke <= (FASTPOKE / 2))
- {
- /*
- * Ask ones that you quickly answer correctly less
- * often!
- */
- randomfactor[correctchar] -= (3 * RANDOMINCBETTER / 2);
- /*
- * Don't let randomfactor hit 0, or you'll NEVER be
- * asked this one AGAIN!
- */
- if (randomfactor[correctchar] < 1)
- randomfactor[correctchar] = 1;
- }
- else if (slowpoke <= FASTPOKE)
- {
- randomfactor[correctchar] -= (RANDOMINCBETTER / 2);
- if (randomfactor[correctchar] < 1)
- randomfactor[correctchar] = 1;
- }
- }
- }
-
- }
- testlength -= testinc;
- behindness -= testinc;
- yourlength -= yourinc;
- }
-
-/*
- * If there are some extra white space characters in the input queue
- * it's OK, we'll get to them next time or we'll clean them out at the
- * end.
- */
- return errorcount;
-}
-
-
-/*----------------------------------------*/
-
-static void
-tone (float hertz, float duration, float amplitude)
-{
- Beep ((int) (duration * 1000), (int) (amplitude * 100), (int) hertz);
-}
-
-
-static void
-toneflush (void)
-{
- BeepWait ();
-}
-
-
-/*----------------------------------------*/
-
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <termio.h>
-struct termio oldtermgtty;
-struct termio termgtty;
-static char *terminal = "/dev/tty";
-static int termfd;
-static int termopen = 0;
-static int oldflgs, newflgs;
-
-static void
-openterminal (void)
-{
- /* get parameters and open terminal */
-
- termfd = open (terminal, O_RDWR | O_NDELAY, 0);
- ioctl (termfd, TCGETA, &termgtty);
- oldtermgtty = termgtty;
- if (typeaway != LETMESEE)
- termgtty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
- termgtty.c_lflag &= ~ICANON;
- termgtty.c_cc[VMIN] = 1;
- termgtty.c_cc[VTIME] = 0;
- ioctl (termfd, TCSETAW, &termgtty);
- termopen = 1;
-}
-
-static int
-readterminal (char **string)
-{
-/* This must be declared static! */
-static char line[TESTBUFSZ];
-int n;
-
- n = read (termfd, line, sizeof (line) - 1);
-
- if (n > 0)
- {
- line[n] = '\0';
- *string = line;
- }
- else
- *string = NULL;
-
- return n;
-}
-
-static void
-closeterminal (void)
-{
- ioctl (termfd, TCSETAW, &oldtermgtty);
- close (termfd);
-}
-
-static void
-die (void)
-{
- cleanup ();
- exit (1);
-}
-
-static void
-cleanup (void)
-{
- if (termopen)
- closeterminal ();
- BeepCleanup ();
-}
-
-static void
-suspend (int sig)
-{
- sigset (SIGTSTP, suspend);
- cleanup ();
- kill (getpid (), SIGSTOP);
- if (termopen)
- openterminal ();
- BeepResume ();
-}
-
-
-/*----------------------------------------*/
-
-static int
-randomletter (void)
-{
-int ii;
-int sum, sum2;
-long ranspot;
-static int lasttime = -1;
-static long norepeat;
-
-/*
- * This keeps the not-so-random random number generator from ignoring
- * certain characters forever!
- */
- norepeat = ((long) time (NULL) / 31) % 17291;
-
-/*
- * All the usable letters get one unit riper.
- */
- for (ii = 0; ii < TWOFIFTYSIX; ii++)
- {
- if (randomfactor[ii] > 0)
- {
-#ifdef DEBUGG
- fprintf (stderr, "%c: %d %d\n",
- (char) ii, randomfactor[ii], randomripe[ii]);
-#endif
- randomripe[ii]++;
- }
- }
-
- sum = 0;
- for (ii = 0; ii < TWOFIFTYSIX; ii++)
- sum += (randomfactor[ii] + (int) (randomripe[ii] / RIPECOUNT));
-
-/*
- * The low bits of random aren't very random, I don't care WHAT
- * the manual claims.
- */
- do
- {
- ranspot = ((lrand48() >> 4) % sum + norepeat) % sum;
-
- sum2 = 0;
- for (ii = 0; ii < TWOFIFTYSIX - 1; ii++)
- {
- sum2 += (randomfactor[ii] + (int) (randomripe[ii] / RIPECOUNT));
-
- if (sum2 > ranspot)
- break;
- }
- /* Do it again if you got the same as last time! */
- } while (ii == lasttime);
-
- /* This one is FRESH again. */
- randomripe[ii] = 0;
- /* Remember for next time. */
- lasttime = ii;
-
- return ii;
-}
-
-static int
-rancomp (const void *elem1, const void *elem2)
-{
- register int *e1 = (int *)elem1;
- register int *e2 = (int *)elem2;
- float a, b;
-
-
- a = (randomfactor[(*e1)] + (randomripe[(*e1)] / (float) RIPECOUNT));
- b = (randomfactor[(*e2)] + (randomripe[(*e2)] / (float) RIPECOUNT));
-
- if (a == b)
- return 0;
- else if (a > b)
- return -1;
- else
- return 1;
-}
-
-static void
-report (void)
-{
- int ii, jj, count;
- float sum;
- int randomstr[TWOFIFTYSIX];
-
- printf ("\nCurrent words per minute: %.1f\n", words_per_minute);
-
- printf ("Total hits %d, misses %d", totalhitcount, totalmisscount);
- if (totalmisscount > 0)
- printf (", hit per miss ratio %.1f\n", (float) totalhitcount / (float) totalmisscount);
- else
- printf ("\n");
-
- if (randomletters)
- {
- printf ("Most to least frequent choices:\n");
- count = 0;
- sum = 0.;
- for (ii = 0; ii < TWOFIFTYSIX; ii++)
- {
- if (randomfactor[ii] > 0)
- {
- sum += (randomfactor[ii] + (randomripe[ii] / (float) RIPECOUNT));
- randomstr[count] = ii;
- count++;
- }
- }
-
- qsort ((char *) randomstr, count, sizeof (randomstr[0]), rancomp);
-
- for (ii = 0; ii < count; ii++)
- {
-/*
- * Insert a space for each jump across an integer.
- * The normalization (count/sum) ensures that if all
- * letters were equally probable, they would all have value 1.
- * Since they are not generally equally probable, then 1 is just the average.
- * Thus the rightmost space in the printout marks where the average is.
- * Further left spaces separate off blocks of letters that are approximately
- * twice as probable as the average, three times, etc.
- */
- if (ii > 0)
- {
- for (jj = 0; jj <
- (int) (
- (randomfactor[randomstr[ii - 1]] + (randomripe[randomstr[ii - 1]] / (float) RIPECOUNT))
- * count / sum) -
- (int) (
- (randomfactor[randomstr[ii]] + (randomripe[randomstr[ii]] / (float) RIPECOUNT))
- * count / sum);
- jj++)
- printf (" ");
- }
- printf ("%c", (char) randomstr[ii]);
- }
- printf ("\n");
- }
-
- /*
- * So you don't get penalized for being "slow" after this.
- */
- if (charbychar)
- slowpoke = SLOWPOKEMAX + 1;
-
- fflush (stdout);
-}