home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume41 / morse / part01 / morse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-19  |  42.9 KB  |  1,803 lines

  1. /*
  2.  * A morse code practice utility. (Contains those characters that can appear
  3.  * on the FCC ham license exam.)
  4.  *
  5.  * Running "morse" without arguments or input gives self-doc.
  6.  *
  7.  * It doesn't keep PERFECT time, but it seems reasonably close
  8.  * for reasonable word speeds on my slow SUN IPC!
  9.  *
  10.  * Joe Dellinger
  11.  * Tue Aug 11 14:01:02 HST 1992
  12.  * University of Hawaii at Manoa
  13.  *
  14.  * Revised by Joe:
  15.  * Thu Nov 26 03:24:19 HST 1992
  16.  *
  17.  * Legal stuff:
  18.  * This code is (ridiculously) heavily modified from morse.c from the Reno UNIX
  19.  * distribution. I (Joe) also used slightly modified versions of a subroutine
  20.  * from Richard Ottolini at Unocal for Sun workstation tone generation.
  21.  * Scott Seligman at Stanford added support for other sorts of devices and
  22.  * made several other changes. John Shalamskas helped test and made comments.
  23.  *
  24.  * I don't think anybody cares if you redistribute this, modify it, etc...
  25.  * But don't claim you wrote it, try to sell it, or take anyone's name out
  26.  * of the code! If you modify it, PLEASE INDICATE WHEN, WHERE, AND HOW
  27.  * YOU MODIFIED IT IN THE COMMENT BELOW. Please.
  28.  */
  29. /*
  30.  * Joe Dellinger, UH Manoa, joe@montebello.soest.hawaii.edu, June 1992:
  31.  *     inserted this sample modification log entry.
  32.  *
  33.  * Marc Unangst, N8VRH, mju@mudos.ann-arbor.mi.us, Dec 1992:
  34.  *     added System V support.
  35.  *
  36.  */
  37.  
  38. /*
  39.  * The following stuff is here because this started life as a routine from
  40.  * the Reno UNIX distribution. (It's also a good boilerplate bit of legalese
  41.  * to have in there anyway.)
  42.  */
  43. /*
  44.  * Copyright (c) 1988 Regents of the University of California.
  45.  * All rights reserved.
  46.  *
  47.  * Redistribution and use in source and binary forms, with or without
  48.  * modification, are permitted provided that the following conditions
  49.  * are met:
  50.  * 1. Redistributions of source code must retain the above copyright
  51.  *    notice, this list of conditions and the following disclaimer.
  52.  * 2. Redistributions in binary form must reproduce the above copyright
  53.  *    notice, this list of conditions and the following disclaimer in the
  54.  *    documentation and/or other materials provided with the distribution.
  55.  * 3. All advertising materials mentioning features or use of this software
  56.  *    must display the following acknowledgement:
  57.  *    This product includes software developed by the University of
  58.  *    California, Berkeley and its contributors.
  59.  * 4. Neither the name of the University nor the names of its contributors
  60.  *    may be used to endorse or promote products derived from this software
  61.  *    without specific prior written permission.
  62.  *
  63.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  64.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  65.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  66.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  67.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  68.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  69.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  70.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  71.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  72.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  73.  * SUCH DAMAGE.
  74.  */
  75.  
  76. #ifndef lint
  77. char            copyright[] =
  78. "@(#) Copyright (c) 1988 Regents of the University of California.\n\
  79.  All rights reserved.\n";
  80. char            copyleftjoe[] =
  81. "@(#) Copyright (c) 1992 Joe Dellinger, University of Hawaii at Manoa.\n";
  82. #endif            /* not lint */
  83.  
  84. /*
  85.  *============================================================================
  86.  * Here starts the code!
  87.  *============================================================================
  88.  */
  89. /*
  90.  * Useful for seeing what the interleaved reading and writing loops are
  91.  * really up to.
  92.  *
  93.  * #define DEBUG
  94.  *
  95.  * If you want to be overwhelmed with information about the probabilities
  96.  * of each letter being chosen.
  97.  *
  98.  * #define DEBUGG
  99.  */
  100.  
  101. #include <stdio.h>
  102. #include <string.h>
  103. #include <sys/types.h>
  104. #include <sys/time.h>
  105. #include <ctype.h>
  106. #include <signal.h>
  107. #include "beep.h"
  108. /* Define USG for termio a la System V. */
  109. #ifdef __hpux
  110. #define USG
  111. #endif
  112. /*
  113.  * Define the return type of a signal handler.  Defaults to void, which is
  114.  * what most modern systems are using these days.
  115.  */
  116. #ifndef SIGRET
  117. #define SIGRET void
  118. #endif
  119.  
  120. #define FREQUENCY    800.
  121. #define FREQUENCY2    602.
  122. #define VOLUME        .5
  123. #define WORDS_PER_MINUTE    20.
  124. #define MAX_BEHINDNESS    0
  125.  
  126. static int      whichfrequ = 0;
  127. static float    frequency1 = FREQUENCY;
  128. static float    frequency2 = FREQUENCY2;
  129. static float    frequency;
  130. static float    volume = VOLUME;
  131. static float    dot_time;
  132. static float    dash_time;
  133. static float    intra_char_time;
  134. static float    inter_char_time;
  135. static float    inter_word_time;
  136. static float    catchup_time;
  137. static int      showletters = 0;
  138. static int      showmorse = 0;
  139. static int      wordsbefore = 0;
  140. static int      wordsafter = 0;
  141. static int      fancyending = 1;
  142. static int      noticebad = 0;
  143. static int      testing = 0;
  144. static int      showtesting = 0;
  145. static int      dynamicspeed = 0;
  146. static int      charbychar = 0;
  147. static int      tryagaincount = 1;
  148. static float    words_per_minute;
  149. static float    fwords_per_minute;
  150. static int      randomletters = 0;
  151. #define LETMESEE 2
  152. static int      typeaway = 0;
  153.  
  154. static int      totalhitcount = 0;
  155. static int      totalmisscount = 0;
  156. static int      helpmeflag = 0;
  157.  
  158. #define MAXWORDLEN    20
  159. #define TESTBUFSZ (MAXWORDLEN*10)
  160. static int      testpointer = -1;
  161. static int      testlength = 0;
  162. static int      behindness = 0;
  163. static int      max_behindness = MAX_BEHINDNESS;
  164. static char     teststring[TESTBUFSZ];
  165. static int      yourpointer = -1;
  166. static int      yourlength = 0;
  167. static char     yourstring[TESTBUFSZ];
  168.  
  169. /*
  170.  * How many times can a given character not be asked before
  171.  * kicking up the probability of asking that one by one randomfactor unit.
  172.  */
  173. #define RIPECOUNT    64
  174.  
  175. #define TWOFIFTYSIX 256
  176. static char    *(code[TWOFIFTYSIX]);
  177. static int      errorlog[TWOFIFTYSIX];
  178. static int      randomfactor[TWOFIFTYSIX];
  179. static int      randomripe[TWOFIFTYSIX];
  180. extern int      testterminal ();
  181. extern int      randomletter ();
  182.  
  183. /*
  184.  * Value of (Wrong - Right), which, if exceeded, will cause the program
  185.  * to start prompting you. Above MAX_ERROR_THRESHOLD it will never prompt.
  186.  * Don't let the user bank too much credit for past correct answers;
  187.  * limit it by min(ERROR_FLOOR, error_threshold).
  188.  */
  189. #define MAX_ERROR_THRESHOLD        1000
  190. #define ERROR_FLOOR            -3
  191. static int      error_threshold = MAX_ERROR_THRESHOLD;
  192. static int      error_floor = ERROR_FLOOR;
  193.  
  194. /*
  195.  * How many characters behind before it decides you're having
  196.  * trouble keeping up.
  197.  */
  198. #define BEHIND        1
  199. #define WAYBEHIND    3
  200. #define TOOFARBEHIND    6
  201. /*
  202.  * If SLOWPOKE or more wpm ticks go by, then it decides you are having lots
  203.  * of trouble remembering this character, and need to be asked it more
  204.  * often.
  205.  */
  206. #define SLOWPOKE    10
  207. /* You aren't slow -- you left and came back! */
  208. #define SLOWPOKEMAX    (50 * SLOWPOKE)
  209. /*
  210.  * If FASTPOKE or less wpm ticks go by, then it decides you are good at this
  211.  * character, and need to be asked it less often.
  212.  */
  213. #define FASTPOKE    4
  214.  
  215. /*
  216.  * These control how quickly the dynamicspeed option acts when you are
  217.  * fast or slow. Easier to slow down than speed up!
  218.  */
  219. #define ERRORSLOWER    1.04
  220. #define ALOTSLOWER    1.15
  221. #define ALITTLESLOWER    1.02
  222. #define ALITTLEFASTER    1.02
  223.  
  224. /*
  225.  * How many inter_char_time's to give you to answer after the end of
  226.  * a word before considering that you are not keeping up.
  227.  * Maximum of 2.3, minimum of 0.
  228.  * The bigger the value, the easier it is to kick in the "automatic
  229.  * speedup" when using the "-d" option. The maximum means you have (almost)
  230.  * right up to the beginning of the next word to answer and still have it
  231.  * count as keeping up.
  232.  */
  233. #define SPORTING_RATIO 1.5
  234.  
  235. /*
  236.  * The bigger, the more evenly things start out.
  237.  * (Must be at least 2)
  238.  */
  239. #define RANDOMBASELEVEL    7
  240. /*
  241.  * RANDOMINCWORSE scales how badly you are punished for being wrong
  242.  * or taking too long. RANDOMINCBETTER scales how you are rewarded for
  243.  * answering quickly or being right.
  244.  */
  245. #define RANDOMINCWORSE    6
  246. #define RANDOMINCBETTER    7
  247. #define RANDOMMAX    (30 * RANDOMBASELEVEL)
  248. /*
  249.  * The average length of a random word (chosen using exponential distribution).
  250.  * After implementing this I'm not so sure an exponential distribution
  251.  * actually models the distribution of real word lengths in English very well.
  252.  * It's not too bad, though, and the words themselves are all garbage anyway,
  253.  * so what the heck.
  254.  */
  255. #define RANDWORDLEN    3.5
  256. /* Put in a newline instead of a space when past this column */
  257. #define RANLINELENGTH    50
  258.  
  259. /* An EOF without the EOF (@) sound */
  260. #define SILENTEOF -2
  261. /* Toggle tone frequency on control-G within input file */
  262. #define FREQU_TOGGLE ((int)'\007')
  263.  
  264. /*
  265.  * If you want the morse code to come out synchronized with the printing
  266.  * of dots and dashes with the -m option, then define this. The problem
  267.  * is that then the morse code then sounds ratty on slower CPU's.
  268.  * John Shalamskas (KJ9U) suggested turning the precise morse-code printing
  269.  * synching off because he didn't like the resulting code quality!
  270.  */
  271. #undef FLUSHCODE
  272.  
  273. /*
  274.  * Choose your favorite random number generator!
  275.  */
  276. #ifndef USERANDOM
  277. #ifndef USELRAND
  278. #ifndef USERAND
  279. #define USERANDOM    /* USELRAND or USERAND are the other choices */
  280. #endif
  281. #endif
  282. #endif
  283.  
  284. #ifdef USERANDOM
  285. #define RANDOM()      random()
  286. #define SEEDRANDOM(s) srandom((int)s)
  287. long            random ();
  288. #endif
  289. #ifdef USELRAND
  290. #define RANDOM()      lrand48()
  291. #define SEEDRANDOM(s) srand48((long)(s))
  292. long            lrand48 ();
  293. #endif
  294. #ifdef USERAND
  295. /*
  296.  * UGH, are you really sure you want to use this one?
  297.  * This one really stinks!
  298.  */
  299. #define RANDOM()      rand()
  300. #define SEEDRANDOM(s) srand((int)(s))
  301. int             rand ();
  302. #endif
  303.  
  304. SIGRET          die (), suspend ();
  305. void            cleanup ();
  306.  
  307. main (argc, argv)
  308.     int             argc;
  309.     char          **argv;
  310. {
  311. extern char    *optarg;
  312. extern int      optind;
  313. int             ch;
  314. char           *p;
  315. int             ii, jj;
  316. int             firsttime, notdoneyet;
  317. int             yourchar;
  318. float           randexp, randnum;
  319. extern time_t   time ();
  320. int             linepos;
  321.  
  322.     if (argc == 1 && isatty (fileno (stdin)))
  323.     {
  324. /*
  325.  * SELF DOC
  326.  */
  327.     printf ("Usage:\n");
  328.     printf ("morse [options] < text_file\n");
  329.     printf ("morse [options] words words words\n");
  330.     printf ("morse [options] -r\n");
  331.     printf ("morse [options] -i\n");
  332.     printf ("Options:\n");
  333.     printf ("-i    Play what you type.\n");
  334.     printf ("-I    Like -i but don't turn off keyboard echoing.\n");
  335.     printf ("-r    Generate random text. Starts out slanted towards easy\n");
  336.     printf ("      letters, then slants towards ones you get wrong.\n");
  337.     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",
  338.         WORDS_PER_MINUTE, FREQUENCY, VOLUME);
  339.     printf ("-g alternate_frequency (default %g)\n      (toggles via control-G in input FILE at a word break)\n", FREQUENCY2);
  340.     printf ("-F Farnsworth_character_words_per_minute\n");
  341.     printf ("-e    leave off the EOT sound at the end\n");
  342.     printf ("-c    complain about illegal characters instead of just ignoring them\n");
  343.     printf ("-b    print each word before doing it\n");
  344.     printf ("-a    print each word after doing it\n");
  345.     printf ("-l    print each letter just before doing it\n");
  346.     printf ("-m    print morse dots and dashes as they sound\n");
  347. #ifdef FLUSHCODE
  348.     printf ("      (this printing-intensive option slows the wpm down!)\n");
  349. #endif
  350.     printf ("-t    Type along with the morse, but don't see what\n");
  351.     printf ("      you're typing (unless you make a mistake).\n");
  352.     printf ("      You are allowed to get ahead as much as you want.\n");
  353.     printf ("      If you get too far behind it will stop and resync with you.\n");
  354.     printf ("      You can force it to resync at the next word end by hitting control-H.\n");
  355.     printf ("      Hit ESC to see how you are doing, control-D to end.\n");
  356.     printf ("-T    Like -t but see your characters (after they are played).\n");
  357.     printf ("-s    Stop after each character and make sure you get it right. (implies -t)\n");
  358.     printf ("-p NUM (default 0)\n");
  359.     printf ("      Make you get it right NUM times, for penance. (implies -s)\n");
  360.     printf ("      (Yes, NUM = 0 means you can sin all you want.)\n");
  361.     printf ("-E NUM  (default %d)\n", MAX_ERROR_THRESHOLD);
  362.     printf ("      If your count of wrong answers minus right answers for a given character\n");
  363.     printf ("      exceeds this, the program will start prompting you.\n");
  364.     printf ("      If %d or above, it will never prompt. (imples -t)\n", MAX_ERROR_THRESHOLD);
  365.     printf ("-M NUM (default %d)\n", MAX_BEHINDNESS);
  366.     printf ("      If you get more than this number of characters behind, pause until you\n");
  367.     printf ("      do your next letter. (1 behind is normal, 0 behind means never pause.)\n");
  368.     printf ("      (implies -t)\n");
  369.     printf ("-d    Dynamically speed up or slow down depending on how you are doing.\n");
  370.     printf ("      (if also -s, then -d _only speeds up_!)\n");
  371.     printf ("\n");
  372.     printf ("\n");
  373.     printf ("\n");
  374.     printf ("For the raw beginner trying to learn morse code I recommend\n");
  375.     printf ("the following sequence:\n");
  376.     printf ("\n");
  377.     printf ("Start learning the alphabet:\n");
  378.     printf ("    morse -r -s -T -d -w 5 -F 15 -p 5 -E -10\n");
  379.     printf ("Then drill drill drill:\n");
  380.     printf ("    morse -r -s -T -d -w 5 -F 15 -p 5 -E 0\n");
  381.     printf ("Real-time drill, with hints if you really need it:\n");
  382.     printf ("    morse -r -T -d -w 5 -F 15 -M 2 -E 4\n");
  383.     printf ("Simulated test:\n");
  384.     printf ("    QSO | morse -e -T -d -w 5 -F 15\n");
  385.     printf ("and the dreaded random-letter test:\n");
  386.     printf ("    morse -r -T -d -w 5 -F 15\n");
  387.     printf ("\n");
  388.     printf ("Written by (mostly) joe@montebello.soest.hawaii.edu\n");
  389.     exit (0);
  390.     }
  391.  
  392.     for (ii = 0; ii < TWOFIFTYSIX; ii++)
  393.     code[ii] = NULL;
  394.  
  395. /* Load in the morse code code */
  396.     code[(int) '0'] = "-----";
  397.     code[(int) '1'] = ".----";
  398.     code[(int) '2'] = "..---";
  399.     code[(int) '3'] = "...--";
  400.     code[(int) '4'] = "....-";
  401.     code[(int) '5'] = ".....";
  402.     code[(int) '6'] = "-....";
  403.     code[(int) '7'] = "--...";
  404.     code[(int) '8'] = "---..";
  405.     code[(int) '9'] = "----.";
  406.  
  407.     code[(int) 'a'] = ".-";
  408.     code[(int) 'b'] = "-...";
  409.     code[(int) 'c'] = "-.-.";
  410.     code[(int) 'd'] = "-..";
  411.     code[(int) 'e'] = ".";
  412.     code[(int) 'f'] = "..-.";
  413.     code[(int) 'g'] = "--.";
  414.     code[(int) 'h'] = "....";
  415.     code[(int) 'i'] = "..";
  416.     code[(int) 'j'] = ".---";
  417.     code[(int) 'k'] = "-.-";
  418.     code[(int) 'l'] = ".-..";
  419.     code[(int) 'm'] = "--";
  420.     code[(int) 'n'] = "-.";
  421.     code[(int) 'o'] = "---";
  422.     code[(int) 'p'] = ".--.";
  423.     code[(int) 'q'] = "--.-";
  424.     code[(int) 'r'] = ".-.";
  425.     code[(int) 's'] = "...";
  426.     code[(int) 't'] = "-";
  427.     code[(int) 'u'] = "..-";
  428.     code[(int) 'v'] = "...-";
  429.     code[(int) 'w'] = ".--";
  430.     code[(int) 'x'] = "-..-";
  431.     code[(int) 'y'] = "-.--";
  432.     code[(int) 'z'] = "--..";
  433.  
  434.     /* Punctuation */
  435.     code[(int) '='] = "-...-";
  436.     code[(int) '?'] = "..--..";
  437.     code[(int) '/'] = "-..-.";
  438.     code[(int) ','] = "--..--";
  439.     code[(int) '.'] = ".-.-.-";
  440.  
  441.     /* Procedural signs */
  442.     code[(int) '+'] = ".-.-.";
  443.     code[(int) '@'] = "...-.-";
  444.  
  445.     for (ii = 0; ii < TWOFIFTYSIX; ii++)
  446.     {
  447.     /* Everything starts equally fresh */
  448.     randomripe[ii] = 0;
  449.     /* Start out assuming you know how everything sounds */
  450.     errorlog[ii] = 0;
  451.  
  452.     if (code[ii] == NULL)
  453.     {
  454.         /* Ensures these will never be chosen */
  455.         randomfactor[ii] = 0;
  456.     }
  457.     else
  458.     {
  459.         /* Start out favoring easy ones */
  460.         randomfactor[ii] = RANDOMBASELEVEL - strlen (code[ii]);
  461.         if (randomfactor[ii] < 1)
  462.         randomfactor[ii] == 1;
  463.     }
  464.     }
  465.  
  466.     words_per_minute = WORDS_PER_MINUTE;
  467.     fwords_per_minute = -1.;
  468.  
  469.     while ((ch = getopt (argc, argv, "F:w:lbamf:g:v:tTdesp:riIcM:E:")) != EOF)
  470.     switch ((char) ch)
  471.     {
  472.     case 'i':
  473.         typeaway = 1;
  474.         break;
  475.     case 'I':
  476.         typeaway = LETMESEE;
  477.         break;
  478.     case 'r':
  479.         randomletters = 1;
  480.         break;
  481.     case 'c':
  482.         noticebad = 1;
  483.         break;
  484.     case 'e':
  485.         fancyending = 0;
  486.         break;
  487.     case 'T':
  488.         testing = 1;
  489.         showtesting = 1;
  490.         break;
  491.     case 't':
  492.         testing = 1;
  493.         break;
  494.     case 's':
  495.         charbychar = 1;
  496.         testing = 1;
  497.         break;
  498.     case 'p':
  499.         charbychar = 1;
  500.         testing = 1;
  501.         sscanf (optarg, "%d", &tryagaincount);
  502.         break;
  503.     case 'M':
  504.         testing = 1;
  505.         sscanf (optarg, "%d", &max_behindness);
  506.         if (max_behindness < 1)
  507.         max_behindness = 0;
  508.         break;
  509.     case 'E':
  510.         testing = 1;
  511.         sscanf (optarg, "%d", &error_threshold);
  512.         if (error_threshold < error_floor)
  513.         error_floor = error_threshold;
  514.         break;
  515.     case 'd':
  516.         dynamicspeed = 1;
  517.         break;
  518.     case 'w':
  519.         sscanf (optarg, "%f", &words_per_minute);
  520.         break;
  521.     case 'F':
  522.         sscanf (optarg, "%f", &fwords_per_minute);
  523.         break;
  524.     case 'l':
  525.         showletters = 1;
  526.         break;
  527.     case 'b':
  528.         wordsbefore = 1;
  529.         break;
  530.     case 'a':
  531.         wordsafter = 1;
  532.         break;
  533.     case 'm':
  534.         showmorse = 1;
  535.         break;
  536.     case 'f':
  537.         sscanf (optarg, "%f", &frequency1);
  538.         break;
  539.     case 'g':
  540.         sscanf (optarg, "%f", &frequency2);
  541.         break;
  542.     case 'v':
  543.         sscanf (optarg, "%f", &volume);
  544.         if (volume < 0.)
  545.         volume = 0.;
  546.         if (volume > 1.)
  547.         volume = 1.;
  548.         break;
  549.     default:
  550.         fprintf (stderr, "Type \"morse\" without arguments to get self-doc!\n");
  551.         exit (1);
  552.         break;
  553.     }
  554.     argc -= optind;
  555.     argv += optind;
  556.  
  557.     if (fwords_per_minute <= 0.)
  558.     fwords_per_minute = words_per_minute;
  559.     new_words_per_minute ();
  560.  
  561.     frequency = frequency1;
  562.  
  563.     if (BeepInit () != 0)
  564.     {
  565.     fprintf (stderr, "Can't access speaker.\n");
  566.     exit (1);
  567.     }
  568.  
  569.     signal (SIGINT, die);
  570.     signal (SIGTERM, die);
  571.     signal (SIGQUIT, die);
  572.     signal (SIGTSTP, suspend);
  573.  
  574.     if (testing || typeaway)
  575.     {
  576.     openterminal ();
  577.     }
  578.  
  579. /*
  580.  * Do .25 seconds of silence initially to give the workstation time to
  581.  * get settled after the stress of starting this program and opening
  582.  * up everything.
  583.  */
  584.     tone (frequency, .25, 0.);
  585.     toneflush ();
  586.  
  587.     if (typeaway)
  588.     {
  589.     testing = 0;
  590.     showtesting = 0;
  591.     charbychar = 0;
  592.     wordsbefore = 0;
  593.     wordsafter = 0;
  594.     randomletters = 0;
  595.  
  596.     notdoneyet = 1;
  597.  
  598.     while (notdoneyet)
  599.     {
  600.         pollyou ();
  601.  
  602.         for (jj = 0; jj < yourlength; jj++)
  603.         {
  604.         yourchar = yourstring[(yourpointer - yourlength + 1 + jj + TESTBUFSZ) % TESTBUFSZ];
  605.  
  606.         /* Control-D: finished */
  607.         if (yourchar == (int) '\004')
  608.         {
  609.             toneflush ();
  610.             notdoneyet = 0;
  611.             break;
  612.         }
  613.  
  614.         if (isspace (yourchar))
  615.         {
  616.             if (showletters)
  617.             {
  618.             toneflush ();
  619.             printf ("%c", yourchar);
  620.             fflush (stdout);
  621.             }
  622.  
  623.             tone (frequency, inter_word_time, 0.);
  624.  
  625.             continue;
  626.         }
  627.  
  628.         morse (yourchar);
  629.         }
  630.         yourlength -= jj;
  631.     }
  632.     }
  633.     else if (randomletters)
  634.     {
  635.     SEEDRANDOM (time (NULL));
  636.     randexp = 1. / (1. - 1. / (float) (RANDWORDLEN));
  637.     linepos = 0;
  638.     while (1)
  639.     {
  640.         dowords (randomletter ());
  641.         linepos++;
  642.  
  643.         /* Knock a few bits off the top so we're sure it won't overflow */
  644.         /* Shift a few bits because the lower bits stink */
  645.         /* Add in the time so it doesn't repeat from run to run */
  646.         randnum = (float) (
  647.                    ((RANDOM () >> 9) + (long) (time (NULL)) >> 4)
  648.                    & 0x00FFFFFF);
  649.         randnum = randnum - randexp * (int) (randnum / randexp);
  650.         if (randnum >= 1.)
  651.         if (linepos >= RANLINELENGTH)
  652.         {
  653.             dowords ((int) '\n');
  654.             linepos = 0;
  655.         }
  656.         else
  657.         {
  658.             dowords ((int) ' ');
  659.             linepos++;
  660.         }
  661.     }
  662.     }
  663.     else
  664.     {
  665.     if (*argv)
  666.     {
  667.         firsttime = 1;
  668.  
  669.         do
  670.         {
  671.         if (!firsttime)
  672.         {
  673.             dowords ((int) ' ');
  674.         }
  675.         else
  676.             firsttime = 0;
  677.  
  678.         for (p = *argv; *p; ++p)
  679.             dowords ((int) *p);
  680.         } while (*++argv);
  681.     }
  682.     else
  683.     {
  684.         while ((ch = getchar ()) != EOF)
  685.         dowords (ch);
  686.     }
  687.     }
  688.  
  689.     if (fancyending)
  690.     dowords (EOF);
  691.     else
  692.     dowords (SILENTEOF);
  693.  
  694.     fflush (stdout);
  695.  
  696.     if (testing)
  697.     {
  698.     /*
  699.      * WE'RE completely done, and YOU aren't! Force catch up. (Note if
  700.      * charbychar = YES we won't get here, since we're always caught up
  701.      * after each character as it comes out.)
  702.      */
  703.     while (testlength > 0)
  704.     {
  705.         tone (frequency, catchup_time, 0.);
  706.         toneflush ();
  707.         testterminal ();
  708.     }
  709.     }
  710.  
  711.     /* Just to be sure! */
  712.     toneflush ();
  713.  
  714.     if (showmorse || wordsbefore || wordsafter || showletters || showtesting)
  715.     printf ("\n");
  716.     fflush (stdout);
  717.  
  718.     if (testing)
  719.     report ();
  720.  
  721. /* If you make any mistakes exit with a return code! */
  722.     cleanup ();
  723.     return (totalmisscount > 0);
  724. }
  725.  
  726. new_words_per_minute ()
  727. {
  728. float           wtick, ftick, tick;
  729.  
  730.     tick = 60. / (words_per_minute * 50);
  731.  
  732.     /*
  733.      * In the limit as wpm goes past fwpm, Farnsworth becomes kosher PARIS
  734.      */
  735.     if (fwords_per_minute <= words_per_minute)
  736.     ftick = 60. / (words_per_minute * 50);
  737.     else
  738.     ftick = 60. / (fwords_per_minute * 50);
  739.  
  740.     wtick = (50. * tick - 31. * ftick) / 19.;
  741.  
  742.     /*
  743.      * This time is used when the computer is waiting on you to hit a key; it
  744.      * is useful to scale the granularity with the real overrall words per
  745.      * minute. This also serves as a measuring rod to see if you are
  746.      * responding "fast enough". If you are too slow, then obviously you are
  747.      * having trouble with that character, and should be given it more OFTEN.
  748.      * Heh heh heh...
  749.      */
  750.     catchup_time = tick;
  751.  
  752.     /*
  753.      * Things between characters and words go at the "remainder" speed,
  754.      * whatever space you need to make the sped-up Farnsworth characters come
  755.      * out with the correct overall words per minute.
  756.      */
  757.     inter_char_time = wtick * 3.;
  758.     inter_word_time = wtick * 7.;
  759.  
  760.     /* Things within the character go at the Farnsworth speed */
  761.     intra_char_time = ftick;
  762.     dot_time = ftick;
  763.     dash_time = ftick * 3.;
  764. }
  765.  
  766. static int      tryingagain = 0, slowpoke = 0;
  767.  
  768. dowords (c)
  769.     int             c;
  770. {
  771. static int      wordc = 0;
  772. static char     word[MAXWORDLEN];
  773. char           *wordp;
  774. int             ii;
  775. int             againcount;
  776. int             are_we_repeating;
  777.  
  778. /*
  779.  * If a word gets too long, just cut it off by inserting a space.
  780.  * Just call ourselves with the character we wish we'd gotten...
  781.  */
  782.     if (wordc == MAXWORDLEN - 1 && !(isspace (c) || c == EOF || c == SILENTEOF || c == FREQU_TOGGLE))
  783.     dowords ((int) ' ');
  784.  
  785.     if (isspace (c) || c == EOF || c == SILENTEOF || c == FREQU_TOGGLE)
  786.     {
  787.     if (wordc > 0)
  788.     {
  789.         word[wordc] = '\0';
  790.  
  791. /*
  792.  * We have just read in a new complete word from the input, (hopefully)
  793.  * during the time of an inter-word space minus an inter-char space.
  794.  * Now let's go back and see what happened with the PREVIOUS word,
  795.  * the one that we had just finished playing. Did the user keep
  796.  * up with us?
  797.  */
  798. #ifdef DEBUG
  799.         fprintf (stderr, " [%d] ", behindness);
  800. #endif
  801.         if (testing && dynamicspeed && !charbychar)
  802.         {
  803.         /*
  804.          * (If charbychar then behindness is ALWAYS 0 at this
  805.          * point...)
  806.          */
  807.         if (behindness == 0)
  808.         {
  809.             /* You're a speed demon! Speed up a bit, then! */
  810.             words_per_minute *= ALITTLEFASTER;
  811.             new_words_per_minute ();
  812.         }
  813.         else if (behindness > WAYBEHIND)
  814.         {
  815.             /* You're way behind! Slow way down. */
  816.             words_per_minute /= ALOTSLOWER;
  817.             new_words_per_minute ();
  818.         }
  819.         else if (behindness > BEHIND)
  820.         {
  821.             /* You're behind! Slow down a bit. */
  822.             words_per_minute /= ALITTLESLOWER;
  823.             new_words_per_minute ();
  824.         }
  825.         }
  826.  
  827.  
  828. /*
  829.  * If the user was WAY too far behind stop and catch up with
  830.  * the "new" word as the first one.
  831.  */
  832.         if (testing && (behindness > TOOFARBEHIND || helpmeflag))
  833.         {
  834.         if (helpmeflag)
  835.             printf ("\nOK, let's restart.\n");
  836.         else
  837.             printf ("\nYou are too far behind! Let's restart.\n");
  838.         fflush (stdout);
  839.  
  840.         toneflush ();
  841.  
  842.         /* Flush the keyboard buffer */
  843.         pollyou ();
  844.  
  845.         /* Forget the past */
  846.         helpmeflag = 0;
  847.         behindness = 0;
  848.         testlength = 0;
  849.         yourlength = 0;
  850.  
  851.         /* Give the user a little rest. */
  852.         sleep (2);
  853.         printf ("\nWPM now %d\n", (int) (words_per_minute + .5));
  854.         fflush (stdout);
  855.         sleep (2);
  856.         printf ("\nREADY?\n");
  857.         fflush (stdout);
  858.         sleep (1);
  859.         printf ("\nSET\n");
  860.         fflush (stdout);
  861.         sleep (1);
  862.         printf ("\nGO!\n");
  863.         fflush (stdout);
  864.         }
  865.  
  866. /*
  867.  * Start treating the new word.
  868.  */
  869.         if (wordsbefore)
  870.         {
  871.         /* Try to keep your out-of-sync text from getting swirled in */
  872.         if (showtesting)
  873.             printf ("\n");
  874.  
  875.         printf ("%s", word);
  876.  
  877.         if (showmorse || showletters || wordsafter || showtesting)
  878.         {
  879.             printf ("  ");
  880.             for (ii = 0; ii < 16 - (wordc + 2); ii++)
  881.             {
  882.             printf (" ");
  883.             }
  884.         }
  885.  
  886.         fflush (stdout);
  887.         }
  888.  
  889.         if (testing && charbychar)
  890.         {
  891.         againcount = 0;
  892.         }
  893.  
  894.         for (wordp = word; *wordp != '\0'; wordp++)
  895.         {
  896.         tryingagain = 0;
  897.  
  898.     tryagain:
  899.         if (testing && !tryingagain && !showletters &&
  900.             error_threshold < MAX_ERROR_THRESHOLD &&
  901.             errorlog[(int) *wordp] > error_threshold)
  902.         {
  903.             toneflush ();
  904.             /* Give them a quick hint */
  905.             printf ("[%c]", *wordp);
  906.             fflush (stdout);
  907.  
  908.             morse (*wordp);
  909.  
  910.             toneflush ();
  911.             if (!showmorse)
  912.             {
  913.             /* Erase the hint */
  914.             printf ("\b\b\b   \b\b\b", *wordp);
  915.             fflush (stdout);
  916.             }
  917.         }
  918.         else
  919.         {
  920.             morse (*wordp);
  921.         }
  922.  
  923.         if (testing)
  924.         {
  925.             if (charbychar)
  926.             {
  927.             toneflush ();
  928.             /* Force catchup */
  929.             slowpoke = 0;
  930.             while (behindness > 0)
  931.             {
  932.                 if (testterminal () && tryagaincount > 0)
  933.                 {
  934.                 /*
  935.                  * OOPS! They got it WRONG! MAKE THEM TRY
  936.                  * AGAIN!
  937.                  */
  938.                 printf ("Try again.\n");
  939.                 /*
  940.                  * Yeah I know gotos are inelegant but I
  941.                  * don't feel like figuring out the "elegant"
  942.                  * way to do this right now.
  943.                  */
  944.                 againcount = tryagaincount - 1;
  945.                 tryingagain = 1;
  946.                 goto tryagain;
  947.                 }
  948.                 else
  949.                 {
  950.                 /*
  951.                  * They got it right, or they didn't answer
  952.                  * yet.
  953.                  */
  954.                 if (behindness > 0)
  955.                 {
  956.                     /*
  957.                      * They are STILL thinking, the
  958.                      * slowpokes. Wait a bit before trying
  959.                      * again.
  960.                      */
  961.                     tone (frequency, catchup_time, 0.);
  962.                     /*
  963.                      * Keep track of how long they're taking
  964.                      * to answer!
  965.                      */
  966.                     if (slowpoke < SLOWPOKEMAX)
  967.                     slowpoke++;
  968.                     toneflush ();
  969.                 }
  970.                 else if (dynamicspeed && !slowpoke && !tryingagain)
  971.                 {
  972.                     /*
  973.                      * They got it right without errors the
  974.                      * first time and we didn't have to wait
  975.                      * for them! A speed demon! Speed up a
  976.                      * bit, then!
  977.                      */
  978.                     words_per_minute *= ALITTLEFASTER;
  979.                     new_words_per_minute ();
  980.                 }
  981.                 }
  982.             }
  983.  
  984.             /* Insufficient penance? */
  985.             if (againcount > 0)
  986.             {
  987.                 againcount--;
  988.                 goto tryagain;
  989.             }
  990.             }
  991.             else
  992.             {
  993.             testterminal ();
  994.             /*
  995.              * Stop if we get more than max_behindness ahead.
  996.              * max_behindness == 0 means don't worry about them,
  997.              * they can be as far behind as they want and we
  998.              * won't stop!
  999.              */
  1000.             if (max_behindness > 0)
  1001.             {
  1002.                 are_we_repeating = 0;
  1003.                 while (behindness >= max_behindness)
  1004.                 {
  1005. #ifdef DEBUG
  1006.                 fprintf (stderr, " (%d) ", behindness);
  1007. #endif
  1008.                 if (are_we_repeating)
  1009.                 {
  1010.                     /*
  1011.                      * Pause for a bit so we don't loop too
  1012.                      * fast
  1013.                      */
  1014.                     tone (frequency, catchup_time, 0.);
  1015.                 }
  1016.                 else
  1017.                 {
  1018.                     are_we_repeating = 1;
  1019.                 }
  1020.                 /* Finish playing whatever we're playing */
  1021.                 toneflush ();
  1022.                 /* And give them another chance */
  1023.                 testterminal ();
  1024.                 }
  1025.             }
  1026.             }
  1027.         }
  1028.         }
  1029.  
  1030.         toneflush ();
  1031.         if (testing)
  1032.         testterminal ();
  1033.  
  1034.         if (wordsafter)
  1035.         {
  1036.         printf (" (%s)", word);
  1037.         }
  1038.  
  1039.         if (wordsbefore || wordsafter || showmorse)
  1040.         printf ("\n");
  1041.         else if (showletters || showtesting)
  1042.         {
  1043.         if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE)
  1044.         {
  1045.             if (showletters)
  1046.             printf ("%c", c);
  1047.  
  1048.             if (showtesting)
  1049.             testaddchar (c);
  1050.         }
  1051.         }
  1052.  
  1053. /*
  1054.  * WHEW! FINISHED QUEUEING THE WORD FOR PLAYING!
  1055.  * Now finish up all the other sundry details...
  1056.  */
  1057.  
  1058.         /* Flush the output printing queue... */
  1059.         fflush (stdout);
  1060.         /*
  1061.          * Pause for a bit; this gives the user a sporting chance at
  1062.          * catching up with us.
  1063.          */
  1064.         tone (frequency, SPORTING_RATIO * inter_char_time, 0.);
  1065.         toneflush ();
  1066.         /* Start sounding an inter-word space */
  1067.         tone (frequency, inter_word_time - SPORTING_RATIO * inter_char_time, 0.);
  1068.  
  1069.         /* While that silence is playing check if the user has caught up. */
  1070.         if (testing)
  1071.         testterminal ();
  1072.  
  1073.         /* We finished this word; reset the word character count */
  1074.         wordc = 0;
  1075.     }
  1076.     else if (!(wordsbefore || wordsafter || showmorse)
  1077.          &&
  1078.          (showletters || showtesting))
  1079.     {
  1080.         if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE)
  1081.         {
  1082.         if (showletters)
  1083.             printf ("%c", c);
  1084.  
  1085.         if (showtesting)
  1086.             testaddchar (c);
  1087.         }
  1088.     }
  1089.  
  1090.     if (c == EOF)
  1091.     {
  1092.         morse (EOF);
  1093.         toneflush ();
  1094.     }
  1095.     else if (c == SILENTEOF)
  1096.     {
  1097.         toneflush ();
  1098.     }
  1099.     else if (c == FREQU_TOGGLE)
  1100.     {
  1101.         /* Switch to the other frequency */
  1102.         /* (Won't work from keyboard, only from a file.) */
  1103.         whichfrequ = 1 - whichfrequ;
  1104.         switch (whichfrequ)
  1105.         {
  1106.         case 1:
  1107.         frequency = frequency2;
  1108.         break;
  1109.         case 0:
  1110.         default:
  1111.         frequency = frequency1;
  1112.         break;
  1113.         }
  1114.     }
  1115.     }
  1116.     else
  1117.     {
  1118.     word[wordc++] = c;
  1119.     }
  1120. }
  1121.  
  1122. /*
  1123.  * Don't try to test the person DURING the call into morse!
  1124.  */
  1125. morse (c)
  1126.     int             c;
  1127. {
  1128.     if (showletters)
  1129.     {
  1130.     if (c == EOF)
  1131.         printf ("EOT");
  1132.     else if (c == '.' && showmorse)
  1133.         printf ("PERIOD");
  1134.     else if (c == '=' && showmorse)
  1135.         printf ("BREAK");
  1136.     else
  1137.         printf ("%c", c);
  1138.  
  1139.     fflush (stdout);
  1140.     }
  1141.  
  1142.     if (isalpha (c))
  1143.     {
  1144.     if (testing)
  1145.         testaddchar (c - (isupper (c) ? 'A' : 'a') + 'a');
  1146.     show (code[c - (isupper (c) ? 'A' : 'a') + 'a']);
  1147.     }
  1148.     else if (c == EOF)
  1149.     {
  1150.     show (code[(int) '@']);
  1151.     }
  1152.     else if (code[c] != NULL)
  1153.     {
  1154.     if (testing)
  1155.         testaddchar (c);
  1156.     show (code[c]);
  1157.     }
  1158.     else
  1159.     {
  1160.     /* Oops! This letter is junk! */
  1161.  
  1162.     if (noticebad)
  1163.     {
  1164.         if (showletters)
  1165.         {
  1166.         fflush (stdout);
  1167.         }
  1168.  
  1169.         /* Simulate a stumble */
  1170.         tone (frequency, 2. * inter_word_time, 0.);
  1171.         toneflush ();
  1172.     }
  1173.  
  1174.     if (showletters)
  1175.     {
  1176.         /* Wipe out what we just printed */
  1177.         fflush (stdout);
  1178.         printf ("\b");
  1179.         printf (" ");
  1180.         printf ("\b");
  1181.         fflush (stdout);
  1182.     }
  1183.  
  1184.     if (noticebad)
  1185.     {
  1186.         if (showletters)
  1187.         {
  1188.         /* And replace it with an error message */
  1189.         printf ("*UNKNOWN_CHARACTER*");
  1190.         fflush (stdout);
  1191.         }
  1192.  
  1193.         /* Give the error call */
  1194.         show ("........");
  1195.  
  1196.         /* Regroup */
  1197.         tone (frequency, inter_word_time, 0.);
  1198.     }
  1199.     }
  1200.  
  1201.     if (showmorse)
  1202.     printf (" ");
  1203.     fflush (stdout);
  1204.     toneflush ();
  1205.     tone (frequency, inter_char_time - intra_char_time, 0.);
  1206. }
  1207.  
  1208.  
  1209. /*
  1210.  * Don't try to test the person WHILE doing dots and dashes!
  1211.  */
  1212. show (s)
  1213.     char           *s;
  1214. {
  1215. char            c;
  1216.  
  1217.     while ((c = *s++) != '\0')
  1218.     {
  1219.     tone (frequency, intra_char_time, 0.);
  1220.  
  1221. #ifdef FLUSHCODE
  1222.     if (showmorse)
  1223.         toneflush ();
  1224. #endif
  1225.  
  1226.     switch (c)
  1227.     {
  1228.     case '.':
  1229.         tone (frequency, dot_time, volume);
  1230.         break;
  1231.     case '-':
  1232.         tone (frequency, dash_time, volume);
  1233.         break;
  1234.     }
  1235.  
  1236.     if (showmorse)
  1237.     {
  1238.         printf ("%c", c);
  1239.         fflush (stdout);
  1240. #ifdef FLUSHCODE
  1241.         toneflush ();
  1242. #endif
  1243.     }
  1244.     }
  1245. }
  1246.  
  1247. /*
  1248.  * This only gets passed valid characters: ones
  1249.  * that have a morse code associated with them
  1250.  * or ones for which isspace(c) is true.
  1251.  */
  1252. testaddchar (c)
  1253.     char            c;
  1254. {
  1255.     testpointer = (testpointer + 1) % TESTBUFSZ;
  1256.     teststring[testpointer] = c;
  1257. #ifdef DEBUG
  1258.     fprintf (stderr, " (%c,%d,%d) ", c, testlength, behindness);
  1259. #endif
  1260.     testlength++;
  1261.     if (testlength > TESTBUFSZ)
  1262.     {
  1263.     fprintf (stderr, "\n\nInput buffer queue overflow! Make TESTBUFSZ bigger!\n");
  1264.     fprintf (stderr, "(Or don't fall so far behind)\n");
  1265.  
  1266.     die ();
  1267.     }
  1268.  
  1269. /*
  1270.  * Since you are never asked to type spaces (you can type them if
  1271.  * you want, but they are ignored) spaces in the input file don't
  1272.  * count against your "behindness".
  1273.  */
  1274.     if (!isspace (c))
  1275.     behindness++;
  1276. }
  1277.  
  1278. youraddchar (c)
  1279.     char            c;
  1280. {
  1281.     yourpointer = (yourpointer + 1) % TESTBUFSZ;
  1282.     yourstring[yourpointer] = c;
  1283. #ifdef DEBUG
  1284.     fprintf (stderr, " <%c,%d> ", c, yourlength);
  1285. #endif
  1286.     yourlength++;
  1287.     if (yourlength > TESTBUFSZ)
  1288.     {
  1289.     fprintf (stderr, "\n\nKeyboard typeahead buffer queue overflow! Make TESTBUFSZ bigger!\n");
  1290.     fprintf (stderr, "(Or don't type so far ahead... how did you expect to get them right anyway?)\n");
  1291.  
  1292.     die ();
  1293.     }
  1294. }
  1295.  
  1296. pollyou ()
  1297. {
  1298. int             ii, num;
  1299. char           *string;
  1300.  
  1301.     num = readterminal (&string);
  1302.  
  1303.     for (ii = 0; ii < num; ii++)
  1304.     youraddchar (string[ii]);
  1305. }
  1306.  
  1307. int
  1308. testterminal ()
  1309. {
  1310. int             testinc, yourinc;
  1311. int             correctchar, yourchar, yourcharnocase;
  1312. int             errorcount;
  1313.  
  1314.     errorcount = 0;
  1315.  
  1316. /*
  1317.  * There is nothing in the input file queue right now,
  1318.  * so we can't process any of your keystrokes.
  1319.  * Defer processing until we can catch up with YOU!
  1320.  */
  1321.     if (testlength == 0)
  1322.     return errorcount;
  1323.  
  1324.     /* We're ready for you; but are you ready for us? */
  1325.     pollyou ();
  1326.  
  1327. /*
  1328.  * Process your entries and the input queue entries in parallel
  1329.  */
  1330.     if (yourlength > 0 && testlength > 0)
  1331.     {
  1332.     for (testinc = 0, yourinc = 0;
  1333.          testinc < testlength && yourinc < yourlength;
  1334.          testinc++, yourinc++)
  1335.     {
  1336.         correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ];
  1337.  
  1338.         /*
  1339.          * The latter half of this if shouldn't be necessary, but just in
  1340.          * case...
  1341.          */
  1342.         if (isspace (correctchar) || code[correctchar] == NULL)
  1343.         {
  1344.         if (showtesting)
  1345.         {
  1346.             printf ("%c", correctchar);
  1347.             fflush (stdout);
  1348.         }
  1349.  
  1350.         /* White space doesn't count for "behindness" */
  1351.         behindness++;
  1352.         /* The _other_ pointer wasn't used; don't increment it. */
  1353.         yourinc--;
  1354.  
  1355.         /* Short circuit the loop */
  1356.         continue;
  1357.         }
  1358.  
  1359.  
  1360.         yourchar = yourstring[(yourpointer - yourlength + 1 + yourinc + TESTBUFSZ) % TESTBUFSZ];
  1361.         if (isalpha (yourchar))
  1362.         yourcharnocase = yourchar - (isupper (yourchar) ? 'A' : 'a') + 'a';
  1363.         else
  1364.         yourcharnocase = yourchar;
  1365.  
  1366.         /* Did you type something rude? If so, just ignore it. */
  1367.         if (isspace (yourchar) || code[yourcharnocase] == NULL)
  1368.         {
  1369.         /* ESCAPE: dump status info */
  1370.         /* Control-D: dump status info and then bye bye */
  1371.         /* Control-H: force restart */
  1372.         if (yourchar == '\033' || yourchar == (int) '\004')
  1373.         {
  1374.             report ();
  1375.  
  1376.             if (yourchar == (int) '\004')
  1377.             die ();
  1378.         }
  1379.         else if (yourchar == '\b')
  1380.         {
  1381.             helpmeflag = 1;
  1382.         }
  1383.  
  1384.         /* The _other_ pointer wasn't used; don't increment it. */
  1385.         testinc--;
  1386.         /* Short circuit the loop */
  1387.         continue;
  1388.         }
  1389.  
  1390.         if (yourcharnocase != correctchar)
  1391.         {
  1392.         errorcount++;
  1393.         totalmisscount++;
  1394.  
  1395.         /*
  1396.          * Record that you are having trouble with these.
  1397.          */
  1398.         errorlog[correctchar]++;
  1399.         if (code[yourcharnocase] != NULL &&
  1400.             errorlog[yourcharnocase] < MAX_ERROR_THRESHOLD)
  1401.             errorlog[yourcharnocase]++;
  1402.  
  1403.         printf ("\n\007%c (%s) for %c (%s)\n",
  1404.             yourchar, code[yourcharnocase],
  1405.             correctchar, code[correctchar]);
  1406.         fflush (stdout);
  1407.  
  1408.         if (charbychar)
  1409.         {
  1410.             /* Give them a bit of time to think about their error */
  1411.             tone (frequency, inter_word_time, 0.);
  1412.             toneflush ();
  1413.         }
  1414.         if (dynamicspeed && !charbychar)
  1415.         {
  1416.             /*
  1417.              * Slow down. Doesn't make sense to slow down for errors,
  1418.              * though, if you've got all the time you want to think
  1419.              * about each one.
  1420.              */
  1421.             words_per_minute /= ERRORSLOWER;
  1422.             new_words_per_minute ();
  1423.         }
  1424.         if (randomletters && !tryingagain)
  1425.         {
  1426.             /*
  1427.              * Ask ones that confused you more often!
  1428.              */
  1429.             if (code[yourcharnocase] != NULL)
  1430.             {
  1431.             randomfactor[yourcharnocase] += (3 * RANDOMINCWORSE / 2);
  1432.             if (randomfactor[yourcharnocase] > RANDOMMAX)
  1433.                 randomfactor[yourcharnocase] = RANDOMMAX;
  1434.             }
  1435.  
  1436.             randomfactor[correctchar] += RANDOMINCWORSE * 2;
  1437.             if (randomfactor[correctchar] > RANDOMMAX)
  1438.             randomfactor[correctchar] = RANDOMMAX;
  1439.         }
  1440.         }
  1441.         else
  1442.         {
  1443.         /*
  1444.          * Record that you got this right.
  1445.          */
  1446.         if (!tryingagain)
  1447.         {
  1448.             totalhitcount++;
  1449.             if (errorlog[correctchar] > error_floor)
  1450.             errorlog[correctchar]--;
  1451.         }
  1452.  
  1453.         if (showtesting)
  1454.         {
  1455.             printf ("%c", yourchar);
  1456.             fflush (stdout);
  1457.         }
  1458.  
  1459.         if (randomletters && !tryingagain)
  1460.         {
  1461.             if (slowpoke == SLOWPOKEMAX)
  1462.             {
  1463.             printf ("\nNice to have you back again, I was getting bored!\n");
  1464.             }
  1465.             else if (slowpoke >= SLOWPOKE * 3)
  1466.             {
  1467.             /*
  1468.              * Did you take too long thinking about it? If so,
  1469.              * you probably need to be asked this one more
  1470.              * often...
  1471.              */
  1472.             randomfactor[correctchar] += (3 * RANDOMINCWORSE / 2);
  1473.             if (randomfactor[correctchar] > RANDOMMAX)
  1474.                 randomfactor[correctchar] = RANDOMMAX;
  1475.  
  1476.             /*
  1477.              * Hits this slow shouldn't count! You were obviously
  1478.              * just guessing! (But it doesn't count as an error
  1479.              * either.)
  1480.              */
  1481.             totalhitcount--;
  1482.             }
  1483.             else if (slowpoke > FASTPOKE)
  1484.             {
  1485.             randomfactor[correctchar] +=
  1486.              (slowpoke * RANDOMINCWORSE) / (2 * SLOWPOKE);
  1487.             if (randomfactor[correctchar] > RANDOMMAX)
  1488.                 randomfactor[correctchar] = RANDOMMAX;
  1489.             }
  1490.             else if (slowpoke <= (FASTPOKE / 2))
  1491.             {
  1492.             /*
  1493.              * Ask ones that you quickly answer correctly less
  1494.              * often!
  1495.              */
  1496.             randomfactor[correctchar] -= (3 * RANDOMINCBETTER / 2);
  1497.             /*
  1498.              * Don't let randomfactor hit 0, or you'll NEVER be
  1499.              * asked this one AGAIN!
  1500.              */
  1501.             if (randomfactor[correctchar] < 1)
  1502.                 randomfactor[correctchar] = 1;
  1503.             }
  1504.             else if (slowpoke <= FASTPOKE)
  1505.             {
  1506.             randomfactor[correctchar] -= (RANDOMINCBETTER / 2);
  1507.             if (randomfactor[correctchar] < 1)
  1508.                 randomfactor[correctchar] = 1;
  1509.             }
  1510.         }
  1511.         }
  1512.  
  1513.     }
  1514.     testlength -= testinc;
  1515.     behindness -= testinc;
  1516.     yourlength -= yourinc;
  1517.     }
  1518.  
  1519. /*
  1520.  * If there are some extra white space characters in the input queue
  1521.  * it's OK, we'll get to them next time or we'll clean them out at the
  1522.  * end.
  1523.  */
  1524.     return errorcount;
  1525. }
  1526.  
  1527.  
  1528. /*----------------------------------------*/
  1529.  
  1530. tone (hertz, duration, amplitude)
  1531.     float           hertz, duration, amplitude;
  1532. {
  1533.     Beep ((int) (duration * 1000), (int) (amplitude * 100), (int) hertz);
  1534. }
  1535.  
  1536.  
  1537. toneflush ()
  1538. {
  1539.     BeepWait ();
  1540. }
  1541.  
  1542.  
  1543. /*----------------------------------------*/
  1544.  
  1545. #include <sys/ioctl.h>
  1546. #include <fcntl.h>
  1547. #ifdef USG
  1548. #include <sys/termio.h>
  1549. struct termio   oldtermgtty;
  1550. struct termio   termgtty;
  1551. #else
  1552. #include <sys/file.h>
  1553. struct sgttyb   oldtermgtty;
  1554. struct sgttyb   termgtty;
  1555. #endif
  1556. static char    *terminal = "/dev/tty";
  1557. static int      termfd;
  1558. static int      oldflgs, newflgs;
  1559. static int      termopen = 0;
  1560.  
  1561. openterminal ()
  1562. {
  1563.     /* get parameters and open terminal */
  1564.  
  1565. #ifdef USG
  1566.     termfd = open (terminal, O_RDWR | O_NDELAY, 0);
  1567.     ioctl (termfd, TCGETA, &termgtty);
  1568.     oldtermgtty = termgtty;
  1569.     if (typeaway != LETMESEE)
  1570.     termgtty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
  1571.     termgtty.c_lflag &= ~ICANON;
  1572.     termgtty.c_cc[VMIN] = 1;
  1573.     termgtty.c_cc[VTIME] = 0;
  1574.     ioctl (0, TCSETAW, &termgtty);
  1575. #else
  1576.     termfd = open (terminal, O_RDWR, 0);
  1577.     ioctl (termfd, TIOCGETP, &termgtty);
  1578.     oldtermgtty = termgtty;
  1579.     if (typeaway != LETMESEE)
  1580.     termgtty.sg_flags &= ~ECHO;
  1581.     termgtty.sg_flags |= CBREAK;
  1582.     ioctl (termfd, TIOCSETP, &termgtty);
  1583.     oldflgs = fcntl (termfd, F_GETFL);
  1584.     newflgs = oldflgs | FNDELAY;
  1585. #endif
  1586.  
  1587.     termopen = 1;
  1588. }
  1589.  
  1590. int
  1591. readterminal (string)
  1592.     char          **string;
  1593. {
  1594. /* This must be declared static! */
  1595. static char     line[TESTBUFSZ];
  1596. int             n;
  1597.  
  1598. #ifndef USG
  1599.     fcntl (termfd, F_SETFL, newflgs);
  1600. #endif
  1601.     n = read (termfd, line, sizeof (line) - 1);
  1602. #ifndef USG
  1603.     fcntl (termfd, F_SETFL, oldflgs);
  1604. #endif
  1605.  
  1606.     if (n > 0)
  1607.     {
  1608.     line[n] = '\0';
  1609.     *string = line;
  1610.     }
  1611.     else
  1612.     *string = NULL;
  1613.  
  1614.     return n;
  1615. }
  1616.  
  1617. closeterminal ()
  1618. {
  1619. #ifdef USG
  1620.     ioctl (termfd, TCSETAW, &oldtermgtty);
  1621. #else
  1622.     ioctl (termfd, TIOCSETP, &oldtermgtty);
  1623. #endif
  1624.     close (termfd);
  1625. }
  1626.  
  1627. SIGRET
  1628. die ()
  1629. {
  1630.     cleanup ();
  1631.     exit (1);
  1632. }
  1633.  
  1634. void
  1635. cleanup ()
  1636. {
  1637.     if (termopen)
  1638.     closeterminal ();
  1639.     BeepCleanup ();
  1640. }
  1641.  
  1642. SIGRET
  1643. suspend ()
  1644. {
  1645.     signal (SIGTSTP, suspend);
  1646.     cleanup ();
  1647.     kill (getpid (), SIGSTOP);
  1648.     if (termopen)
  1649.     openterminal ();
  1650.     BeepResume ();
  1651. }
  1652.  
  1653.  
  1654. /*----------------------------------------*/
  1655.  
  1656. int
  1657. randomletter ()
  1658. {
  1659. int             ii;
  1660. int             sum, sum2;
  1661. long            ranspot;
  1662. extern time_t   time ();
  1663. static int      lasttime = -1;
  1664. static long     norepeat;
  1665.  
  1666. /*
  1667.  * This keeps the not-so-random random number generator from ignoring
  1668.  * certain characters forever!
  1669.  */
  1670.     norepeat = ((long) time (NULL) / 31) % 17291;
  1671.  
  1672. /*
  1673.  * All the usable letters get one unit riper.
  1674.  */
  1675.     for (ii = 0; ii < TWOFIFTYSIX; ii++)
  1676.     {
  1677.     if (randomfactor[ii] > 0)
  1678.     {
  1679. #ifdef DEBUGG
  1680.         fprintf (stderr, "%c: %d %d\n",
  1681.              (char) ii, randomfactor[ii], randomripe[ii]);
  1682. #endif
  1683.         randomripe[ii]++;
  1684.     }
  1685.     }
  1686.  
  1687.     sum = 0;
  1688.     for (ii = 0; ii < TWOFIFTYSIX; ii++)
  1689.     sum += (randomfactor[ii] + (int) (randomripe[ii] / RIPECOUNT));
  1690.  
  1691. /*
  1692.  * The low bits of random aren't very random, I don't care WHAT
  1693.  * the manual claims.
  1694.  */
  1695.     do
  1696.     {
  1697.     ranspot = ((RANDOM () >> 4) % sum + norepeat) % sum;
  1698.  
  1699.     sum2 = 0;
  1700.     for (ii = 0; ii < TWOFIFTYSIX - 1; ii++)
  1701.     {
  1702.         sum2 += (randomfactor[ii] + (int) (randomripe[ii] / RIPECOUNT));
  1703.  
  1704.         if (sum2 > ranspot)
  1705.         break;
  1706.     }
  1707.     /* Do it again if you got the same as last time! */
  1708.     } while (ii == lasttime);
  1709.  
  1710.     /* This one is FRESH again. */
  1711.     randomripe[ii] = 0;
  1712.     /* Remember for next time. */
  1713.     lasttime = ii;
  1714.  
  1715.     return ii;
  1716. }
  1717.  
  1718. report ()
  1719. {
  1720. int             ii, jj, count;
  1721. float           sum;
  1722. int             randomstr[TWOFIFTYSIX];
  1723. extern int      rancomp ();
  1724.  
  1725.     printf ("\nCurrent words per minute: %.1f\n", words_per_minute);
  1726.  
  1727.     printf ("Total hits %d, misses %d", totalhitcount, totalmisscount);
  1728.     if (totalmisscount > 0)
  1729.     printf (", hit per miss ratio %.1f\n", (float) totalhitcount / (float) totalmisscount);
  1730.     else
  1731.     printf ("\n");
  1732.  
  1733.     if (randomletters)
  1734.     {
  1735.     printf ("Most to least frequent choices:\n");
  1736.     count = 0;
  1737.     sum = 0.;
  1738.     for (ii = 0; ii < TWOFIFTYSIX; ii++)
  1739.     {
  1740.         if (randomfactor[ii] > 0)
  1741.         {
  1742.         sum += (randomfactor[ii] + (randomripe[ii] / (float) RIPECOUNT));
  1743.         randomstr[count] = ii;
  1744.         count++;
  1745.         }
  1746.     }
  1747.  
  1748.     qsort ((char *) randomstr, count, sizeof (randomstr[0]), rancomp);
  1749.  
  1750.     for (ii = 0; ii < count; ii++)
  1751.     {
  1752. /*
  1753.  * Insert a space for each jump across an integer.
  1754.  * The normalization (count/sum) ensures that if all
  1755.  * letters were equally probable, they would all have value 1.
  1756.  * Since they are not generally equally probable, then 1 is just the average.
  1757.  * Thus the rightmost space in the printout marks where the average is.
  1758.  * Further left spaces separate off blocks of letters that are approximately
  1759.  * twice as probable as the average, three times, etc.
  1760.  */
  1761.         if (ii > 0)
  1762.         {
  1763.         for (jj = 0; jj <
  1764.              (int) (
  1765.                 (randomfactor[randomstr[ii - 1]] + (randomripe[randomstr[ii - 1]] / (float) RIPECOUNT))
  1766.                 * count / sum) -
  1767.              (int) (
  1768.                 (randomfactor[randomstr[ii]] + (randomripe[randomstr[ii]] / (float) RIPECOUNT))
  1769.                 * count / sum);
  1770.              jj++)
  1771.             printf (" ");
  1772.         }
  1773.         printf ("%c", (char) randomstr[ii]);
  1774.     }
  1775.     printf ("\n");
  1776.     }
  1777.  
  1778.     /*
  1779.      * So you don't get penalized for being "slow" after this.
  1780.      */
  1781.     if (charbychar)
  1782.     slowpoke = SLOWPOKEMAX + 1;
  1783.  
  1784.     fflush (stdout);
  1785. }
  1786.  
  1787. int
  1788. rancomp (elem1, elem2)
  1789.     int            *elem1, *elem2;
  1790. {
  1791. float           a, b;
  1792.  
  1793.     a = (randomfactor[(*elem1)] + (randomripe[(*elem1)] / (float) RIPECOUNT));
  1794.     b = (randomfactor[(*elem2)] + (randomripe[(*elem2)] / (float) RIPECOUNT));
  1795.  
  1796.     if (a == b)
  1797.     return 0;
  1798.     else if (a > b)
  1799.     return -1;
  1800.     else
  1801.     return 1;
  1802. }
  1803.