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 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 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 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 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 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 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 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 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 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