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 35static 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 40static char sccsid[] = "@(#)morse.c 8.1 (Berkeley) 5/31/93"; 41#endif 42static const char rcsid[] = 43 "$FreeBSD: stable/11/usr.bin/morse/morse.c 330981 2018-03-15 09:38:18Z eadler $"; 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 72struct morsetab { 73 const char inchar; 74 const char *morse; 75}; 76 77static 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 */ 155static 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 */ 175static 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 */ 238static 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 275static void show(const char *), play(const char *), morse(char); 276static void decode (char *), fdecode(FILE *); 277static void ttyout(const char *); 278static void sighandler(int); 279 280static int pflag, lflag, rflag, sflag, eflag; 281static int wpm = 20; /* effective words per minute */ 282static int cpm; /* effective words per minute between 283 * characters */ 284#define FREQUENCY 600 285static int freq = FREQUENCY; 286static 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) 291static float dot_clock; 292static float cdot_clock; 293static int spkr, line; 294static struct termios otty, ntty; 295static int olflags; 296 297#ifdef SPEAKER 298static 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 309static const struct morsetab *hightab; 310 311int 312main(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 470static void 471morse(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 504static void 505show(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 519static void 520play(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 559static void 560ttyout(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 601void 602fdecode(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 638void 639decode(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 663static void 664sighandler(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