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