]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/morse/morse.c
OpenSSL: update to 3.0.12
[FreeBSD/FreeBSD.git] / usr.bin / 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 static const char copyright[] =
36 "@(#) Copyright (c) 1988, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38
39 #if 0
40 static char sccsid[] = "@(#)morse.c     8.1 (Berkeley) 5/31/93";
41 #endif
42
43 #include <sys/time.h>
44 #include <sys/ioctl.h>
45
46 #include <ctype.h>
47 #include <err.h>
48 #include <fcntl.h>
49 #include <langinfo.h>
50 #include <locale.h>
51 #include <signal.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <termios.h>
56 #include <unistd.h>
57
58 #ifdef __FreeBSD__
59 /* Always use the speaker, let the open fail if -p is selected */
60 #define SPEAKER "/dev/speaker"
61 #endif
62
63 #define WHITESPACE " \t\n"
64 #define DELIMITERS " \t"
65
66 #ifdef SPEAKER
67 #include <dev/speaker/speaker.h>
68 #endif
69
70 struct morsetab {
71         const char      inchar;
72         const char     *morse;
73 };
74
75 static const struct morsetab mtab[] = {
76
77         /* letters */
78
79         {'a', ".-"},
80         {'b', "-..."},
81         {'c', "-.-."},
82         {'d', "-.."},
83         {'e', "."},
84         {'f', "..-."},
85         {'g', "--."},
86         {'h', "...."},
87         {'i', ".."},
88         {'j', ".---"},
89         {'k', "-.-"},
90         {'l', ".-.."},
91         {'m', "--"},
92         {'n', "-."},
93         {'o', "---"},
94         {'p', ".--."},
95         {'q', "--.-"},
96         {'r', ".-."},
97         {'s', "..."},
98         {'t', "-"},
99         {'u', "..-"},
100         {'v', "...-"},
101         {'w', ".--"},
102         {'x', "-..-"},
103         {'y', "-.--"},
104         {'z', "--.."},
105
106         /* digits */
107
108         {'0', "-----"},
109         {'1', ".----"},
110         {'2', "..---"},
111         {'3', "...--"},
112         {'4', "....-"},
113         {'5', "....."},
114         {'6', "-...."},
115         {'7', "--..."},
116         {'8', "---.."},
117         {'9', "----."},
118
119         /* punctuation */
120
121         {',', "--..--"},
122         {'.', ".-.-.-"},
123         {'"', ".-..-."},
124         {'!', "..--."},
125         {'?', "..--.."},
126         {'/', "-..-."},
127         {'-', "-....-"},
128         {'=', "-...-"},         /* BT */
129         {':', "---..."},
130         {';', "-.-.-."},
131         {'(', "-.--."},         /* KN */
132         {')', "-.--.-"},
133         {'$', "...-..-"},
134         {'+', ".-.-."},         /* AR */
135         {'@', ".--.-."},        /* AC */
136         {'_', "..--.-"},
137         {'\'', ".----."},
138
139         /* prosigns without already assigned values */
140
141         {'#', ".-..."},         /* AS */
142         {'&', "...-.-"},        /* SK */
143         {'*', "...-."},         /* VE */
144         {'%', "-...-.-"},       /* BK */
145
146         {'\0', ""}
147 };
148
149 /*
150  * Code-points for some Latin1 chars in ISO-8859-1 encoding.
151  * UTF-8 encoded chars in the comments.
152  */
153 static const struct morsetab iso8859_1tab[] = {
154         {'\340', ".--.-"},      /* à */
155         {'\341', ".--.-"},      /* á */
156         {'\342', ".--.-"},      /* â */
157         {'\344', ".-.-"},       /* ä */
158         {'\347', "-.-.."},      /* ç */
159         {'\350', "..-.."},      /* è */
160         {'\351', "..-.."},      /* é */
161         {'\352', "-..-."},      /* ê */
162         {'\361', "--.--"},      /* ñ */
163         {'\366', "---."},       /* ö */
164         {'\374', "..--"},       /* ü */
165
166         {'\0', ""}
167 };
168
169 /*
170  * Code-points for some Greek chars in ISO-8859-7 encoding.
171  * UTF-8 encoded chars in the comments.
172  */
173 static const struct morsetab iso8859_7tab[] = {
174         /*
175          * This table does not implement:
176          * - the special sequences for the seven diphthongs,
177          * - the punctuation differences.
178          * Implementing these features would introduce too many
179          * special-cases in the program's main loop.
180          * The diphthong sequences are:
181          * alpha iota           .-.-
182          * alpha upsilon        ..--
183          * epsilon upsilon      ---.
184          * eta upsilon          ...-
185          * omicron iota         ---..
186          * omicron upsilon      ..-
187          * upsilon iota         .---
188          * The different punctuation symbols are:
189          * ;    ..-.-
190          * !    --..--
191          */
192         {'\341', ".-"},         /* α, alpha */
193         {'\334', ".-"},         /* ά, alpha with acute */
194         {'\342', "-..."},       /* β, beta */
195         {'\343', "--."},        /* γ, gamma */
196         {'\344', "-.."},        /* δ, delta */
197         {'\345', "."},          /* ε, epsilon */
198         {'\335', "."},          /* έ, epsilon with acute */
199         {'\346', "--.."},       /* ζ, zeta */
200         {'\347', "...."},       /* η, eta */
201         {'\336', "...."},       /* ή, eta with acute */
202         {'\350', "-.-."},       /* θ, theta */
203         {'\351', ".."},         /* ι, iota */
204         {'\337', ".."},         /* ί, iota with acute */
205         {'\372', ".."},         /* ϊ, iota with diaeresis */
206         {'\300', ".."},         /* ΐ, iota with acute and diaeresis */
207         {'\352', "-.-"},        /* κ, kappa */
208         {'\353', ".-.."},       /* λ, lambda */
209         {'\354', "--"},         /* μ, mu */
210         {'\355', "-."},         /* ν, nu */
211         {'\356', "-..-"},       /* ξ, xi */
212         {'\357', "---"},        /* ο, omicron */
213         {'\374', "---"},        /* ό, omicron with acute */
214         {'\360', ".--."},       /* π, pi */
215         {'\361', ".-."},        /* ρ, rho */
216         {'\363', "..."},        /* σ, sigma */
217         {'\362', "..."},        /* ς, final sigma */
218         {'\364', "-"},          /* τ, tau */
219         {'\365', "-.--"},       /* υ, upsilon */
220         {'\375', "-.--"},       /* ύ, upsilon with acute */
221         {'\373', "-.--"},       /* ϋ, upsilon and diaeresis */
222         {'\340', "-.--"},       /* ΰ, upsilon with acute and diaeresis */
223         {'\366', "..-."},       /* φ, phi */
224         {'\367', "----"},       /* χ, chi */
225         {'\370', "--.-"},       /* ψ, psi */
226         {'\371', ".--"},        /* ω, omega */
227         {'\376', ".--"},        /* ώ, omega with acute */
228
229         {'\0', ""}
230 };
231
232 /*
233  * Code-points for the Cyrillic alphabet in KOI8-R encoding.
234  * UTF-8 encoded chars in the comments.
235  */
236 static const struct morsetab koi8rtab[] = {
237         {'\301', ".-"},         /* а, a */
238         {'\302', "-..."},       /* б, be */
239         {'\327', ".--"},        /* в, ve */
240         {'\307', "--."},        /* г, ge */
241         {'\304', "-.."},        /* д, de */
242         {'\305', "."},          /* е, ye */
243         {'\243', "."},          /* ё, yo, the same as ye */
244         {'\326', "...-"},       /* ж, she */
245         {'\332', "--.."},       /* з, ze */
246         {'\311', ".."},         /* и, i */
247         {'\312', ".---"},       /* й, i kratkoye */
248         {'\313', "-.-"},        /* к, ka */
249         {'\314', ".-.."},       /* л, el */
250         {'\315', "--"},         /* м, em */
251         {'\316', "-."},         /* н, en */
252         {'\317', "---"},        /* о, o */
253         {'\320', ".--."},       /* п, pe */
254         {'\322', ".-."},        /* р, er */
255         {'\323', "..."},        /* с, es */
256         {'\324', "-"},          /* т, te */
257         {'\325', "..-"},        /* у, u */
258         {'\306', "..-."},       /* ф, ef */
259         {'\310', "...."},       /* х, kha */
260         {'\303', "-.-."},       /* ц, ce */
261         {'\336', "---."},       /* ч, che */
262         {'\333', "----"},       /* ш, sha */
263         {'\335', "--.-"},       /* щ, shcha */
264         {'\331', "-.--"},       /* ы, yi */
265         {'\330', "-..-"},       /* ь, myakhkij znak */
266         {'\334', "..-.."},      /* э, ae */
267         {'\300', "..--"},       /* ю, yu */
268         {'\321', ".-.-"},       /* я, ya */
269
270         {'\0', ""}
271 };
272
273 static void     show(const char *), play(const char *), morse(char);
274 static void     decode (char *), fdecode(FILE *);
275 static void     ttyout(const char *);
276 static void     sighandler(int);
277
278 static int      pflag, lflag, rflag, sflag, eflag;
279 static int      wpm = 20;       /* effective words per minute */
280 static int      cpm;            /* effective words per minute between
281                                  * characters */
282 #define FREQUENCY 600
283 static int      freq = FREQUENCY;
284 static char     *device;        /* for tty-controlled generator */
285
286 #define DASH_LEN 3
287 #define CHAR_SPACE 3
288 #define WORD_SPACE (7 - CHAR_SPACE - 1)
289 static float    dot_clock;
290 static float    cdot_clock;
291 static int      spkr, line;
292 static struct termios otty, ntty;
293 static int      olflags;
294
295 #ifdef SPEAKER
296 static tone_t   sound;
297 #define GETOPTOPTS "c:d:ef:lprsw:"
298 #define USAGE \
299 "usage: morse [-elprs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
300 #else
301 #define GETOPTOPTS "c:d:ef:lrsw:"
302 #define USAGE \
303 "usage: morse [-elrs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
304
305 #endif
306
307 static const struct morsetab *hightab;
308
309 int
310 main(int argc, char *argv[])
311 {
312         int    ch, lflags;
313         char  *p, *codeset;
314
315         while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
316                 switch ((char) ch) {
317                 case 'c':
318                         cpm = atoi(optarg);
319                         break;
320                 case 'd':
321                         device = optarg;
322                         break;
323                 case 'e':
324                         eflag = 1;
325                         setvbuf(stdout, 0, _IONBF, 0);
326                         break;
327                 case 'f':
328                         freq = atoi(optarg);
329                         break;
330                 case 'l':
331                         lflag = 1;
332                         break;
333 #ifdef SPEAKER
334                 case 'p':
335                         pflag = 1;
336                         break;
337 #endif
338                 case 'r':
339                         rflag = 1;
340                         break;
341                 case 's':
342                         sflag = 1;
343                         break;
344                 case 'w':
345                         wpm = atoi(optarg);
346                         break;
347                 case '?':
348                 default:
349                         errx(1, USAGE);
350                 }
351         if ((sflag && lflag) || (sflag && rflag) || (lflag && rflag)) {
352                 errx(1, "morse: only one of -l, -s, and -r allowed\n");
353         }
354         if ((pflag || device) && (sflag || lflag)) {
355                 errx(1, "morse: only one of -p, -d and -l, -s allowed\n");
356         }
357         if (cpm == 0) {
358                 cpm = wpm;
359         }
360         if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) {
361                 errx(1, "morse: insane speed\n");
362         }
363         if ((pflag || device) && (freq == 0)) {
364                 freq = FREQUENCY;
365         }
366 #ifdef SPEAKER
367         if (pflag) {
368                 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
369                         err(1, SPEAKER);
370                 }
371         } else
372 #endif
373         if (device) {
374                 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
375                         err(1, "open tty line");
376                 }
377                 if (tcgetattr(line, &otty) == -1) {
378                         err(1, "tcgetattr() failed");
379                 }
380                 ntty = otty;
381                 ntty.c_cflag |= CLOCAL;
382                 tcsetattr(line, TCSANOW, &ntty);
383                 lflags = fcntl(line, F_GETFL);
384                 lflags &= ~O_NONBLOCK;
385                 fcntl(line, F_SETFL, &lflags);
386                 ioctl(line, TIOCMGET, &lflags);
387                 lflags &= ~TIOCM_RTS;
388                 olflags = lflags;
389                 ioctl(line, TIOCMSET, &lflags);
390                 (void)signal(SIGHUP, sighandler);
391                 (void)signal(SIGINT, sighandler);
392                 (void)signal(SIGQUIT, sighandler);
393                 (void)signal(SIGTERM, sighandler);
394         }
395         if (pflag || device) {
396                 dot_clock = wpm / 2.4;          /* dots/sec */
397                 dot_clock = 1 / dot_clock;      /* duration of a dot */
398                 dot_clock = dot_clock / 2;      /* dot_clock runs at twice */
399                                                 /* the dot rate */
400                 dot_clock = dot_clock * 100;    /* scale for ioctl */
401
402                 cdot_clock = cpm / 2.4;         /* dots/sec */
403                 cdot_clock = 1 / cdot_clock;    /* duration of a dot */
404                 cdot_clock = cdot_clock / 2;    /* dot_clock runs at twice */
405                                                 /* the dot rate */
406                 cdot_clock = cdot_clock * 100;  /* scale for ioctl */
407         }
408
409         argc -= optind;
410         argv += optind;
411
412         if (setlocale(LC_CTYPE, "") != NULL &&
413             *(codeset = nl_langinfo(CODESET)) != '\0') {
414                 if (strcmp(codeset, "KOI8-R") == 0)
415                         hightab = koi8rtab;
416                 else if (strcmp(codeset, "ISO8859-1") == 0 ||
417                          strcmp(codeset, "ISO8859-15") == 0)
418                         hightab = iso8859_1tab;
419                 else if (strcmp(codeset, "ISO8859-7") == 0)
420                         hightab = iso8859_7tab;
421         }
422
423         if (lflag) {
424                 printf("m");
425         }
426         if (rflag) {
427                 if (*argv) {
428                         do {
429                                 p = strtok(*argv, DELIMITERS);
430                                 if (p == NULL) {
431                                         decode(*argv);
432                                 }
433                                 else {
434                                         while (p) {
435                                                 decode(p);
436                                                 p = strtok(NULL, DELIMITERS);
437                                         }
438                                 }
439                         } while (*++argv);
440                         putchar('\n');
441                 } else {
442                         fdecode(stdin);
443                 }
444         }
445         else if (*argv) {
446                 do {
447                         for (p = *argv; *p; ++p) {
448                                 if (eflag)
449                                         putchar(*p);
450                                 morse(*p);
451                         }
452                         if (eflag)
453                                 putchar(' ');
454                         morse(' ');
455                 } while (*++argv);
456         } else {
457                 while ((ch = getchar()) != EOF) {
458                         if (eflag)
459                                 putchar(ch);
460                         morse(ch);
461                 }
462         }
463         if (device)
464                 tcsetattr(line, TCSANOW, &otty);
465         exit(0);
466 }
467
468 static void
469 morse(char c)
470 {
471         const struct morsetab *m;
472
473         if (isalpha((unsigned char)c))
474                 c = tolower((unsigned char)c);
475         if ((c == '\r') || (c == '\n'))
476                 c = ' ';
477         if (c == ' ') {
478                 if (pflag)
479                         play(" ");
480                 else if (device)
481                         ttyout(" ");
482                 else if (lflag)
483                         printf("\n");
484                 else
485                         show("");
486                 return;
487         }
488         for (m = ((unsigned char)c < 0x80? mtab: hightab);
489              m != NULL && m->inchar != '\0';
490              m++) {
491                 if (m->inchar == c) {
492                         if (pflag) {
493                                 play(m->morse);
494                         } else if (device) {
495                                 ttyout(m->morse);
496                         } else
497                                 show(m->morse);
498                 }
499         }
500 }
501
502 static void
503 show(const char *s)
504 {
505         if (lflag) {
506                 printf("%s ", s);
507         } else if (sflag) {
508                 printf(" %s\n", s);
509         } else {
510                 for (; *s; ++s)
511                         printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" :
512                             "di" : "dah");
513                 printf("\n");
514         }
515 }
516
517 static void
518 play(const char *s)
519 {
520 #ifdef SPEAKER
521         const char *c;
522
523         for (c = s; *c != '\0'; c++) {
524                 switch (*c) {
525                 case '.':
526                         sound.frequency = freq;
527                         sound.duration = dot_clock;
528                         break;
529                 case '-':
530                         sound.frequency = freq;
531                         sound.duration = dot_clock * DASH_LEN;
532                         break;
533                 case ' ':
534                         sound.frequency = 0;
535                         sound.duration = cdot_clock * WORD_SPACE;
536                         break;
537                 default:
538                         sound.duration = 0;
539                 }
540                 if (sound.duration) {
541                         if (ioctl(spkr, SPKRTONE, &sound) == -1) {
542                                 err(1, "ioctl play");
543                         }
544                 }
545                 sound.frequency = 0;
546                 sound.duration = dot_clock;
547                 if (ioctl(spkr, SPKRTONE, &sound) == -1) {
548                         err(1, "ioctl rest");
549                 }
550         }
551         sound.frequency = 0;
552         sound.duration = cdot_clock * CHAR_SPACE;
553         ioctl(spkr, SPKRTONE, &sound);
554 #endif
555 }
556
557 static void
558 ttyout(const char *s)
559 {
560         const char *c;
561         int duration, on, lflags;
562
563         for (c = s; *c != '\0'; c++) {
564                 switch (*c) {
565                 case '.':
566                         on = 1;
567                         duration = dot_clock;
568                         break;
569                 case '-':
570                         on = 1;
571                         duration = dot_clock * DASH_LEN;
572                         break;
573                 case ' ':
574                         on = 0;
575                         duration = cdot_clock * WORD_SPACE;
576                         break;
577                 default:
578                         on = 0;
579                         duration = 0;
580                 }
581                 if (on) {
582                         ioctl(line, TIOCMGET, &lflags);
583                         lflags |= TIOCM_RTS;
584                         ioctl(line, TIOCMSET, &lflags);
585                 }
586                 duration *= 10000;
587                 if (duration)
588                         usleep(duration);
589                 ioctl(line, TIOCMGET, &lflags);
590                 lflags &= ~TIOCM_RTS;
591                 ioctl(line, TIOCMSET, &lflags);
592                 duration = dot_clock * 10000;
593                 usleep(duration);
594         }
595         duration = cdot_clock * CHAR_SPACE * 10000;
596         usleep(duration);
597 }
598
599 void
600 fdecode(FILE *stream)
601 {
602         char *n, *p, *s;
603         char buf[BUFSIZ];
604
605         s = buf;
606         while (fgets(s, BUFSIZ - (s - buf), stream)) {
607                 p = buf;
608
609                 while (*p && isblank(*p)) {
610                         p++;
611                 }
612                 while (*p && isspace(*p)) {
613                         p++;
614                         putchar (' ');
615                 }
616                 while (*p) {
617                         n = strpbrk(p, WHITESPACE);
618                         if (n == NULL) {
619                                 /* The token was interrupted at the end
620                                  * of the buffer. Shift it to the begin
621                                  * of the buffer.
622                                  */
623                                 for (s = buf; *p; *s++ = *p++)
624                                         ;
625                         } else {
626                                 *n = '\0';
627                                 n++;
628                                 decode(p);
629                                 p = n;
630                         }
631                 }
632         }
633         putchar('\n');
634 }
635
636 void
637 decode(char *p)
638 {
639         char c;
640         const struct morsetab *m;
641
642         c = ' ';
643         for (m = mtab; m != NULL && m->inchar != '\0'; m++) {
644                 if (strcmp(m->morse, p) == 0) {
645                         c = m->inchar;
646                         break;
647                 }
648         }
649
650         if (c == ' ')
651                 for (m = hightab; m != NULL && m->inchar != '\0'; m++) {
652                         if (strcmp(m->morse, p) == 0) {
653                                 c = m->inchar;
654                                 break;
655                         }
656                 }
657
658         putchar(c);
659 }
660
661 static void
662 sighandler(int signo)
663 {
664
665         ioctl(line, TIOCMSET, &olflags);
666         tcsetattr(line, TCSANOW, &otty);
667
668         signal(signo, SIG_DFL);
669         (void)kill(getpid(), signo);
670 }