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