~ubuntu-branches/ubuntu/trusty/morse/trusty

« back to all changes in this revision

Viewing changes to .pc/03morse/morse.d/morse.c

  • Committer: Bazaar Package Importer
  • Author(s): Nanakos Chrysostomos
  • Date: 2010-05-19 01:04:44 UTC
  • Revision ID: james.westby@ubuntu.com-20100519010444-frth5pbu83x5gs3w
Tags: 2.1-4
* New package maintainer. (Closes: Bug#553991).
* Lintian cleaning.
* Updated S-V to 3.8.4
    - debian/copyright file is now UTF-8 encoded.
* Added co-authors to debian/copyright file.
* Added ${misc:Depends} to conrol file.
* Added debian/source/format file.
* Removed "-" from rules file because the package uses
  a static makefile.
* Bumped debhelper versioned Build-Depends to 7, and updated
  debian/compat.
* Extracted changes to upstream files to debian/patches.

Show diffs side-by-side

added added

removed removed

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