]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - games/morse/morse.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / games / morse / morse.c
1 /*
2  * Copyright (c) 1988, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 /*
31  * Taught to send *real* morse by Lyndon Nerenberg (VE6BBM)
32  * <lyndon@orthanc.ca>
33  */
34
35 #ifndef lint
36 static const char copyright[] =
37 "@(#) Copyright (c) 1988, 1993\n\
38         The Regents of the University of California.  All rights reserved.\n";
39 #endif /* not lint */
40
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)morse.c     8.1 (Berkeley) 5/31/93";
44 #endif
45 static const char rcsid[] =
46  "$FreeBSD$";
47 #endif /* not lint */
48
49 #include <sys/time.h>
50
51 #include <ctype.h>
52 #include <fcntl.h>
53 #include <langinfo.h>
54 #include <locale.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <termios.h>
60 #include <unistd.h>
61
62 /* Always use the speaker, let the open fail if -p is selected */
63 #define SPEAKER "/dev/speaker"
64
65 #ifdef SPEAKER
66 #include <dev/speaker/speaker.h>
67 #endif
68
69 struct morsetab {
70         const char      inchar;
71         const char     *morse;
72 };
73
74 static const struct morsetab mtab[] = {
75
76         /* letters */
77
78         {'a', ".-"},
79         {'b', "-..."},
80         {'c', "-.-."},
81         {'d', "-.."},
82         {'e', "."},
83         {'f', "..-."},
84         {'g', "--."},
85         {'h', "...."},
86         {'i', ".."},
87         {'j', ".---"},
88         {'k', "-.-"},
89         {'l', ".-.."},
90         {'m', "--"},
91         {'n', "-."},
92         {'o', "---"},
93         {'p', ".--."},
94         {'q', "--.-"},
95         {'r', ".-."},
96         {'s', "..."},
97         {'t', "-"},
98         {'u', "..-"},
99         {'v', "...-"},
100         {'w', ".--"},
101         {'x', "-..-"},
102         {'y', "-.--"},
103         {'z', "--.."},
104
105         /* digits */
106
107         {'0', "-----"},
108         {'1', ".----"},
109         {'2', "..---"},
110         {'3', "...--"},
111         {'4', "....-"},
112         {'5', "....."},
113         {'6', "-...."},
114         {'7', "--..."},
115         {'8', "---.."},
116         {'9', "----."},
117
118         /* punctuation */
119
120         {',', "--..--"},
121         {'.', ".-.-.-"},
122         {'"', ".-..-."},
123         {'!', "..--."},
124         {'?', "..--.."},
125         {'/', "-..-."},
126         {'-', "-....-"},
127         {'=', "-...-"},         /* BT */
128         {':', "---..."},
129         {';', "-.-.-."},
130         {'(', "-.--."},         /* KN */
131         {')', "-.--.-"},
132         {'$', "...-..-"},
133         {'+', ".-.-."},         /* AR */
134         {'@', ".--.-."},        /* AC */
135
136         /* prosigns without already assigned values */
137
138         {'#', ".-..."},         /* AS */
139         {'&', "...-.-"},        /* SK */
140         {'*', "...-."},         /* VE */
141         {'%', "-...-.-"},       /* BK */
142
143         {'\0', ""}
144 };
145
146 /*
147  * Code-points for some Latin1 chars in ISO-8859-1 encoding.
148  * UTF-8 encoded chars in the comments.
149  */
150 static const struct morsetab iso8859_1tab[] = {
151         {'\340', ".--.-"},      /* à */
152         {'\341', ".--.-"},      /* á */
153         {'\342', ".--.-"},      /* â */
154         {'\344', ".-.-"},       /* ä */
155         {'\347', "-.-.."},      /* ç */
156         {'\350', "..-.."},      /* è */
157         {'\351', "..-.."},      /* é */
158         {'\352', "-..-."},      /* ê */
159         {'\366', "---."},       /* ö */
160         {'\374', "..--"},       /* ü */
161
162         {'\0', ""}
163 };
164
165 /*
166  * Code-points for some Greek chars in ISO-8859-7 encoding.
167  * UTF-8 encoded chars in the comments.
168  */
169 static const struct morsetab iso8859_7tab[] = {
170         /*
171          * This table does not implement:
172          * - the special sequences for the seven diphthongs,
173          * - the punctuation differences.
174          * Implementing these features would introduce too many
175          * special-cases in the program's main loop.
176          * The diphthong sequences are:
177          * alpha iota           .-.-
178          * alpha upsilon        ..--
179          * epsilon upsilon      ---.
180          * eta upsilon          ...-
181          * omicron iota         ---..
182          * omicron upsilon      ..-
183          * upsilon iota         .---
184          * The different punctuation symbols are:
185          * ;    ..-.-
186          * !    --..--
187          */
188         {'\341', ".-"},         /* α, alpha */
189         {'\334', ".-"},         /* ά, alpha with acute */
190         {'\342', "-..."},       /* β, beta */
191         {'\343', "--."},        /* γ, gamma */
192         {'\344', "-.."},        /* δ, delta */
193         {'\345', "."},          /* ε, epsilon */
194         {'\335', "."},          /* έ, epsilon with acute */
195         {'\346', "--.."},       /* ζ, zeta */
196         {'\347', "...."},       /* η, eta */
197         {'\336', "...."},       /* ή, eta with acute */
198         {'\350', "-.-."},       /* θ, theta */
199         {'\351', ".."},         /* ι, iota */
200         {'\337', ".."},         /* ί, iota with acute */
201         {'\372', ".."},         /* ϊ, iota with diaeresis */
202         {'\300', ".."},         /* ΐ, iota with acute and diaeresis */
203         {'\352', "-.-"},        /* κ, kappa */
204         {'\353', ".-.."},       /* λ, lambda */
205         {'\354', "--"},         /* μ, mu */
206         {'\355', "-."},         /* ν, nu */
207         {'\356', "-..-"},       /* ξ, xi */
208         {'\357', "---"},        /* ο, omicron */
209         {'\374', "---"},        /* ό, omicron with acute */
210         {'\360', ".--."},       /* π, pi */
211         {'\361', ".-."},        /* ρ, rho */
212         {'\363', "..."},        /* σ, sigma */
213         {'\362', "..."},        /* ς, final sigma */
214         {'\364', "-"},          /* τ, tau */
215         {'\365', "-.--"},       /* υ, upsilon */
216         {'\375', "-.--"},       /* ύ, upsilon with acute */
217         {'\373', "-.--"},       /* ϋ, upsilon and diaeresis */
218         {'\340', "-.--"},       /* ΰ, upsilon with acute and diaeresis */
219         {'\366', "..-."},       /* φ, phi */
220         {'\367', "----"},       /* χ, chi */
221         {'\370', "--.-"},       /* ψ, psi */
222         {'\371', ".--"},        /* ω, omega */
223         {'\376', ".--"},        /* ώ, omega with acute */
224
225         {'\0', ""}
226 };
227
228 /*
229  * Code-points for the Cyrillic alphabet in KOI8-R encoding.
230  * UTF-8 encoded chars in the comments.
231  */
232 static const struct morsetab koi8rtab[] = {
233         {'\301', ".-"},         /* а, a */
234         {'\302', "-..."},       /* б, be */
235         {'\327', ".--"},        /* в, ve */
236         {'\307', "--."},        /* г, ge */
237         {'\304', "-.."},        /* д, de */
238         {'\305', "."},          /* е, ye */
239         {'\243', "."},          /* ё, yo, the same as ye */
240         {'\326', "...-"},       /* ж, she */
241         {'\332', "--.."},       /* з, ze */
242         {'\311', ".."},         /* и, i */
243         {'\312', ".---"},       /* й, i kratkoye */
244         {'\313', "-.-"},        /* к, ka */
245         {'\314', ".-.."},       /* л, el */
246         {'\315', "--"},         /* м, em */
247         {'\316', "-."},         /* н, en */
248         {'\317', "---"},        /* о, o */
249         {'\320', ".--."},       /* п, pe */
250         {'\322', ".-."},        /* р, er */
251         {'\323', "..."},        /* с, es */
252         {'\324', "-"},          /* т, te */
253         {'\325', "..-"},        /* у, u */
254         {'\306', "..-."},       /* ф, ef */
255         {'\310', "...."},       /* х, kha */
256         {'\303', "-.-."},       /* ц, ce */
257         {'\336', "---."},       /* ч, che */
258         {'\333', "----"},       /* ш, sha */
259         {'\335', "--.-"},       /* щ, shcha */
260         {'\331', "-.--"},       /* ы, yi */
261         {'\330', "-..-"},       /* ь, myakhkij znak */
262         {'\334', "..-.."},      /* э, ae */
263         {'\300', "..--"},       /* ю, yu */
264         {'\321', ".-.-"},       /* я, ya */
265
266         {'\0', ""}
267 };
268
269 static void     show(const char *), play(const char *), morse(char);
270 static void     ttyout(const char *);
271 static void     sighandler(int);
272
273 #define GETOPTOPTS "c:d:ef:lsw:"
274 #define USAGE \
275 "usage: morse [-els] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
276
277 static int      pflag, lflag, sflag, eflag;
278 static int      wpm = 20;       /* effective words per minute */
279 static int      cpm;            /* effective words per minute between
280                                  * characters */
281 #define FREQUENCY 600
282 static int      freq = FREQUENCY;
283 static char     *device;        /* for tty-controlled generator */
284
285 #define DASH_LEN 3
286 #define CHAR_SPACE 3
287 #define WORD_SPACE (7 - CHAR_SPACE - 1)
288 static float    dot_clock;
289 static float    cdot_clock;
290 static int      spkr, line;
291 static struct termios otty, ntty;
292 static int      olflags;
293
294 #ifdef SPEAKER
295 static tone_t   sound;
296 #undef GETOPTOPTS
297 #define GETOPTOPTS "c:d:ef:lpsw:"
298 #undef USAGE
299 #define USAGE \
300 "usage: morse [-elps] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
301 #endif
302
303 static const struct morsetab *hightab;
304
305 int
306 main(int argc, char **argv)
307 {
308         int    ch, lflags;
309         char  *p, *codeset;
310
311         while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
312                 switch ((char) ch) {
313                 case 'c':
314                         cpm = atoi(optarg);
315                         break;
316                 case 'd':
317                         device = optarg;
318                         break;
319                 case 'e':
320                         eflag = 1;
321                         setvbuf(stdout, 0, _IONBF, 0);
322                         break;
323                 case 'f':
324                         freq = atoi(optarg);
325                         break;
326                 case 'l':
327                         lflag = 1;
328                         break;
329 #ifdef SPEAKER
330                 case 'p':
331                         pflag = 1;
332                         break;
333 #endif
334                 case 's':
335                         sflag = 1;
336                         break;
337                 case 'w':
338                         wpm = atoi(optarg);
339                         break;
340                 case '?':
341                 default:
342                         fputs(USAGE, stderr);
343                         exit(1);
344                 }
345         if (sflag && lflag) {
346                 fputs("morse: only one of -l and -s allowed\n", stderr);
347                 exit(1);
348         }
349         if ((pflag || device) && (sflag || lflag)) {
350                 fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr);
351                 exit(1);
352         }
353         if (cpm == 0)
354                 cpm = wpm;
355         if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) {
356                 fputs("morse: insane speed\n", stderr);
357                 exit(1);
358         }
359         if ((pflag || device) && (freq == 0))
360                 freq = FREQUENCY;
361
362 #ifdef SPEAKER
363         if (pflag) {
364                 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
365                         perror(SPEAKER);
366                         exit(1);
367                 }
368         } else
369 #endif
370         if (device) {
371                 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
372                         perror("open tty line");
373                         exit(1);
374                 }
375                 if (tcgetattr(line, &otty) == -1) {
376                         perror("tcgetattr() failed");
377                         exit(1);
378                 }
379                 ntty = otty;
380                 ntty.c_cflag |= CLOCAL;
381                 tcsetattr(line, TCSANOW, &ntty);
382                 lflags = fcntl(line, F_GETFL);
383                 lflags &= ~O_NONBLOCK;
384                 fcntl(line, F_SETFL, &lflags);
385                 ioctl(line, TIOCMGET, &lflags);
386                 lflags &= ~TIOCM_RTS;
387                 olflags = lflags;
388                 ioctl(line, TIOCMSET, &lflags);
389                 (void)signal(SIGHUP, sighandler);
390                 (void)signal(SIGINT, sighandler);
391                 (void)signal(SIGQUIT, sighandler);
392                 (void)signal(SIGTERM, sighandler);
393         }
394         if (pflag || device) {
395                 dot_clock = wpm / 2.4;          /* dots/sec */
396                 dot_clock = 1 / dot_clock;      /* duration of a dot */
397                 dot_clock = dot_clock / 2;      /* dot_clock runs at twice */
398                                                 /* the dot rate */
399                 dot_clock = dot_clock * 100;    /* scale for ioctl */
400
401                 cdot_clock = cpm / 2.4;         /* dots/sec */
402                 cdot_clock = 1 / cdot_clock;    /* duration of a dot */
403                 cdot_clock = cdot_clock / 2;    /* dot_clock runs at twice */
404                                                 /* the dot rate */
405                 cdot_clock = cdot_clock * 100;  /* scale for ioctl */
406         }
407
408         argc -= optind;
409         argv += optind;
410
411         if (setlocale(LC_CTYPE, "") != NULL &&
412             *(codeset = nl_langinfo(CODESET)) != '\0') {
413                 if (strcmp(codeset, "KOI8-R") == 0)
414                         hightab = koi8rtab;
415                 else if (strcmp(codeset, "ISO8859-1") == 0 ||
416                          strcmp(codeset, "ISO8859-15") == 0)
417                         hightab = iso8859_1tab;
418                 else if (strcmp(codeset, "ISO8859-7") == 0)
419                         hightab = iso8859_7tab;
420         }
421
422         if (lflag)
423                 printf("m");
424         if (*argv) {
425                 do {
426                         for (p = *argv; *p; ++p) {
427                                 if (eflag)
428                                         putchar(*p);
429                                 morse(*p);
430                         }
431                         if (eflag)
432                                 putchar(' ');
433                         morse(' ');
434                 } while (*++argv);
435         } else {
436                 while ((ch = getchar()) != EOF) {
437                         if (eflag)
438                                 putchar(ch);
439                         morse(ch);
440                 }
441         }
442         if (device)
443                 tcsetattr(line, TCSANOW, &otty);
444         exit(0);
445 }
446
447 static void
448 morse(char c)
449 {
450         const struct morsetab *m;
451
452         if (isalpha((unsigned char)c))
453                 c = tolower((unsigned char)c);
454         if ((c == '\r') || (c == '\n'))
455                 c = ' ';
456         if (c == ' ') {
457                 if (pflag)
458                         play(" ");
459                 else if (device)
460                         ttyout(" ");
461                 else if (lflag)
462                         printf("\n");
463                 else
464                         show("");
465                 return;
466         }
467         for (m = ((unsigned char)c < 0x80? mtab: hightab);
468              m != NULL && m->inchar != '\0';
469              m++) {
470                 if (m->inchar == c) {
471                         if (pflag) {
472                                 play(m->morse);
473                         } else if (device) {
474                                 ttyout(m->morse);
475                         } else
476                                 show(m->morse);
477                 }
478         }
479 }
480
481 static void
482 show(const char *s)
483 {
484         if (lflag) {
485                 printf("%s ", s);
486         } else if (sflag) {
487                 printf(" %s\n", s);
488         } else {
489                 for (; *s; ++s)
490                         printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" :
491                             "di" : "dah");
492                 printf("\n");
493         }
494 }
495
496 static void
497 play(const char *s)
498 {
499 #ifdef SPEAKER
500         const char *c;
501
502         for (c = s; *c != '\0'; c++) {
503                 switch (*c) {
504                 case '.':
505                         sound.frequency = freq;
506                         sound.duration = dot_clock;
507                         break;
508                 case '-':
509                         sound.frequency = freq;
510                         sound.duration = dot_clock * DASH_LEN;
511                         break;
512                 case ' ':
513                         sound.frequency = 0;
514                         sound.duration = cdot_clock * WORD_SPACE;
515                         break;
516                 default:
517                         sound.duration = 0;
518                 }
519                 if (sound.duration) {
520                         if (ioctl(spkr, SPKRTONE, &sound) == -1) {
521                                 perror("ioctl play");
522                                 exit(1);
523                         }
524                 }
525                 sound.frequency = 0;
526                 sound.duration = dot_clock;
527                 if (ioctl(spkr, SPKRTONE, &sound) == -1) {
528                         perror("ioctl rest");
529                         exit(1);
530                 }
531         }
532         sound.frequency = 0;
533         sound.duration = cdot_clock * CHAR_SPACE;
534         ioctl(spkr, SPKRTONE, &sound);
535 #endif
536 }
537
538 static void
539 ttyout(const char *s)
540 {
541         const char *c;
542         int duration, on, lflags;
543
544         for (c = s; *c != '\0'; c++) {
545                 switch (*c) {
546                 case '.':
547                         on = 1;
548                         duration = dot_clock;
549                         break;
550                 case '-':
551                         on = 1;
552                         duration = dot_clock * DASH_LEN;
553                         break;
554                 case ' ':
555                         on = 0;
556                         duration = cdot_clock * WORD_SPACE;
557                         break;
558                 default:
559                         on = 0;
560                         duration = 0;
561                 }
562                 if (on) {
563                         ioctl(line, TIOCMGET, &lflags);
564                         lflags |= TIOCM_RTS;
565                         ioctl(line, TIOCMSET, &lflags);
566                 }
567                 duration *= 10000;
568                 if (duration)
569                         usleep(duration);
570                 ioctl(line, TIOCMGET, &lflags);
571                 lflags &= ~TIOCM_RTS;
572                 ioctl(line, TIOCMSET, &lflags);
573                 duration = dot_clock * 10000;
574                 usleep(duration);
575         }
576         duration = cdot_clock * CHAR_SPACE * 10000;
577         usleep(duration);
578 }
579
580 static void
581 sighandler(int signo)
582 {
583
584         ioctl(line, TIOCMSET, &olflags);
585         tcsetattr(line, TCSANOW, &otty);
586
587         signal(signo, SIG_DFL);
588         (void)kill(getpid(), signo);
589 }