morse.c revision 203479
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. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34/* 35 * Taught to send *real* morse by Lyndon Nerenberg (VE6BBM) 36 * <lyndon@orthanc.ca> 37 */ 38 39#ifndef lint 40static const char copyright[] = 41"@(#) Copyright (c) 1988, 1993\n\ 42 The Regents of the University of California. All rights reserved.\n"; 43#endif /* not lint */ 44 45#ifndef lint 46#if 0 47static char sccsid[] = "@(#)morse.c 8.1 (Berkeley) 5/31/93"; 48#endif 49static const char rcsid[] = 50 "$FreeBSD: head/games/morse/morse.c 203479 2010-02-04 07:08:06Z imp $"; 51#endif /* not lint */ 52 53#include <sys/time.h> 54 55#include <ctype.h> 56#include <fcntl.h> 57#include <langinfo.h> 58#include <locale.h> 59#include <signal.h> 60#include <stdio.h> 61#include <stdlib.h> 62#include <string.h> 63#include <termios.h> 64#include <unistd.h> 65 66/* Always use the speaker, let the open fail if -p is selected */ 67#define SPEAKER "/dev/speaker" 68 69#ifdef SPEAKER 70#include <dev/speaker/speaker.h> 71#endif 72 73struct morsetab { 74 char inchar; 75 char *morse; 76}; 77 78static const struct morsetab mtab[] = { 79 80 /* letters */ 81 82 {'a', ".-"}, 83 {'b', "-..."}, 84 {'c', "-.-."}, 85 {'d', "-.."}, 86 {'e', "."}, 87 {'f', "..-."}, 88 {'g', "--."}, 89 {'h', "...."}, 90 {'i', ".."}, 91 {'j', ".---"}, 92 {'k', "-.-"}, 93 {'l', ".-.."}, 94 {'m', "--"}, 95 {'n', "-."}, 96 {'o', "---"}, 97 {'p', ".--."}, 98 {'q', "--.-"}, 99 {'r', ".-."}, 100 {'s', "..."}, 101 {'t', "-"}, 102 {'u', "..-"}, 103 {'v', "...-"}, 104 {'w', ".--"}, 105 {'x', "-..-"}, 106 {'y', "-.--"}, 107 {'z', "--.."}, 108 109 /* digits */ 110 111 {'0', "-----"}, 112 {'1', ".----"}, 113 {'2', "..---"}, 114 {'3', "...--"}, 115 {'4', "....-"}, 116 {'5', "....."}, 117 {'6', "-...."}, 118 {'7', "--..."}, 119 {'8', "---.."}, 120 {'9', "----."}, 121 122 /* punctuation */ 123 124 {',', "--..--"}, 125 {'.', ".-.-.-"}, 126 {'"', ".-..-."}, 127 {'!', "..--."}, 128 {'?', "..--.."}, 129 {'/', "-..-."}, 130 {'-', "-....-"}, 131 {'=', "-...-"}, /* BT */ 132 {':', "---..."}, 133 {';', "-.-.-."}, 134 {'(', "-.--."}, /* KN */ 135 {')', "-.--.-"}, 136 {'$', "...-..-"}, 137 {'+', ".-.-."}, /* AR */ 138 {'@', ".--.-."}, /* AC */ 139 140 /* prosigns without already assigned values */ 141 142 {'#', ".-..."}, /* AS */ 143 {'&', "...-.-"}, /* SK */ 144 {'*', "...-."}, /* VE */ 145 {'%', "-...-.-"}, /* BK */ 146 147 {'\0', ""} 148}; 149 150 151static const struct morsetab iso8859_1tab[] = { 152 {'�', ".--.-"}, 153 {'�', ".--.-"}, 154 {'�', ".--.-"}, 155 {'�', ".-.-"}, 156 {'�', "-.-.."}, 157 {'�', "..-.."}, 158 {'�', "..-.."}, 159 {'�', "-..-."}, 160 {'�', "---."}, 161 {'�', "..--"}, 162 163 {'\0', ""} 164}; 165 166static const struct morsetab iso8859_7tab[] = { 167 /* 168 * The greek alphabet; you'll need an 8859-7 font in order 169 * to see the actual characters. 170 * This table does not implement: 171 * - the special sequences for the seven diphthongs, 172 * - the punctuation differences. 173 * Implementing these features would introduce too many 174 * special-cases in the program's main loop. 175 * The diphtong sequences are: 176 * alpha iota .-.- 177 * alpha upsilon ..-- 178 * epsilon upsilon ---. 179 * eta upsilon ...- 180 * omikron iota ---.. 181 * omikron upsilon ..- 182 * upsilon iota .--- 183 * The different punctuation symbols are: 184 * ; ..-.- 185 * ! --..-- 186 */ 187 {'�', ".-"}, /* alpha */ 188 {'�', ".-"}, /* alpha with acute */ 189 {'�', "-..."}, /* beta */ 190 {'�', "--."}, /* gamma */ 191 {'�', "-.."}, /* delta */ 192 {'�', "."}, /* epsilon */ 193 {'�', "."}, /* epsilon with acute */ 194 {'�', "--.."}, /* zeta */ 195 {'�', "...."}, /* eta */ 196 {'�', "...."}, /* eta with acute */ 197 {'�', "-.-."}, /* theta */ 198 {'�', ".."}, /* iota */ 199 {'�', ".."}, /* iota with acute */ 200 {'�', ".."}, /* iota with diairesis */ 201 {'�', ".."}, /* iota with acute and diairesis */ 202 {'�', "-.-"}, /* kappa */ 203 {'�', ".-.."}, /* lamda */ 204 {'�', "--"}, /* mu */ 205 {'�', "-."}, /* nu */ 206 {'�', "-..-"}, /* xi */ 207 {'�', "---"}, /* omicron */ 208 {'�', "---"}, /* omicron with acute */ 209 {'�', ".--."}, /* pi */ 210 {'�', ".-."}, /* rho */ 211 {'�', "..."}, /* sigma */ 212 {'�', "..."}, /* final sigma */ 213 {'�', "-"}, /* tau */ 214 {'�', "-.--"}, /* upsilon */ 215 {'�', "-.--"}, /* upsilon with acute */ 216 {'�', "-.--"}, /* upsilon and diairesis */ 217 {'�', "-.--"}, /* upsilon with acute and diairesis */ 218 {'�', "..-."}, /* phi */ 219 {'�', "----"}, /* chi */ 220 {'�', "--.-"}, /* psi */ 221 {'�', ".--"}, /* omega */ 222 {'�', ".--"}, /* omega with acute */ 223 224 {'\0', ""} 225}; 226 227static const struct morsetab koi8rtab[] = { 228 /* 229 * the cyrillic alphabet; you'll need a KOI8R font in order 230 * to see the actual characters 231 */ 232 {'�', ".-"}, /* a */ 233 {'�', "-..."}, /* be */ 234 {'�', ".--"}, /* ve */ 235 {'�', "--."}, /* ge */ 236 {'�', "-.."}, /* de */ 237 {'�', "."}, /* ye */ 238 {'�', "."}, /* yo, the same as ye */ 239 {'�', "...-"}, /* she */ 240 {'�', "--.."}, /* ze */ 241 {'�', ".."}, /* i */ 242 {'�', ".---"}, /* i kratkoye */ 243 {'�', "-.-"}, /* ka */ 244 {'�', ".-.."}, /* el */ 245 {'�', "--"}, /* em */ 246 {'�', "-."}, /* en */ 247 {'�', "---"}, /* o */ 248 {'�', ".--."}, /* pe */ 249 {'�', ".-."}, /* er */ 250 {'�', "..."}, /* es */ 251 {'�', "-"}, /* te */ 252 {'�', "..-"}, /* u */ 253 {'�', "..-."}, /* ef */ 254 {'�', "...."}, /* kha */ 255 {'�', "-.-."}, /* ce */ 256 {'�', "---."}, /* che */ 257 {'�', "----"}, /* sha */ 258 {'�', "--.-"}, /* shcha */ 259 {'�', "-.--"}, /* yi */ 260 {'�', "-..-"}, /* myakhkij znak */ 261 {'�', "..-.."}, /* ae */ 262 {'�', "..--"}, /* yu */ 263 {'�', ".-.-"}, /* ya */ 264 265 {'\0', ""} 266}; 267 268void show(const char *), play(const char *), morse(char); 269void ttyout(const char *); 270void sighandler(int); 271 272#define GETOPTOPTS "c:d:ef:lsw:" 273#define USAGE \ 274"usage: morse [-els] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n" 275 276static int pflag, lflag, sflag, eflag; 277static int wpm = 20; /* effective words per minute */ 278static int cpm; /* effective words per minute between 279 * characters */ 280#define FREQUENCY 600 281static int freq = FREQUENCY; 282static char *device; /* for tty-controlled generator */ 283 284#define DASH_LEN 3 285#define CHAR_SPACE 3 286#define WORD_SPACE (7 - CHAR_SPACE - 1) 287static float dot_clock; 288static float cdot_clock; 289int spkr, line; 290struct termios otty, ntty; 291int olflags; 292 293#ifdef SPEAKER 294tone_t sound; 295#undef GETOPTOPTS 296#define GETOPTOPTS "c:d:ef:lpsw:" 297#undef USAGE 298#define USAGE \ 299"usage: morse [-elps] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n" 300#endif 301 302static const struct morsetab *hightab; 303 304int 305main(int argc, char **argv) 306{ 307 int ch, lflags; 308 char *p, *codeset; 309 310 while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1) 311 switch ((char) ch) { 312 case 'c': 313 cpm = atoi(optarg); 314 break; 315 case 'd': 316 device = optarg; 317 break; 318 case 'e': 319 eflag = 1; 320 setvbuf(stdout, 0, _IONBF, 0); 321 break; 322 case 'f': 323 freq = atoi(optarg); 324 break; 325 case 'l': 326 lflag = 1; 327 break; 328#ifdef SPEAKER 329 case 'p': 330 pflag = 1; 331 break; 332#endif 333 case 's': 334 sflag = 1; 335 break; 336 case 'w': 337 wpm = atoi(optarg); 338 break; 339 case '?': 340 default: 341 fputs(USAGE, stderr); 342 exit(1); 343 } 344 if (sflag && lflag) { 345 fputs("morse: only one of -l and -s allowed\n", stderr); 346 exit(1); 347 } 348 if ((pflag || device) && (sflag || lflag)) { 349 fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr); 350 exit(1); 351 } 352 if (cpm == 0) 353 cpm = wpm; 354 if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) { 355 fputs("morse: insane speed\n", stderr); 356 exit(1); 357 } 358 if ((pflag || device) && (freq == 0)) 359 freq = FREQUENCY; 360 361#ifdef SPEAKER 362 if (pflag) { 363 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) { 364 perror(SPEAKER); 365 exit(1); 366 } 367 } else 368#endif 369 if (device) { 370 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) { 371 perror("open tty line"); 372 exit(1); 373 } 374 if (tcgetattr(line, &otty) == -1) { 375 perror("tcgetattr() failed"); 376 exit(1); 377 } 378 ntty = otty; 379 ntty.c_cflag |= CLOCAL; 380 tcsetattr(line, TCSANOW, &ntty); 381 lflags = fcntl(line, F_GETFL); 382 lflags &= ~O_NONBLOCK; 383 fcntl(line, F_SETFL, &lflags); 384 ioctl(line, TIOCMGET, &lflags); 385 lflags &= ~TIOCM_RTS; 386 olflags = lflags; 387 ioctl(line, TIOCMSET, &lflags); 388 (void)signal(SIGHUP, sighandler); 389 (void)signal(SIGINT, sighandler); 390 (void)signal(SIGQUIT, sighandler); 391 (void)signal(SIGTERM, sighandler); 392 } 393 if (pflag || device) { 394 dot_clock = wpm / 2.4; /* dots/sec */ 395 dot_clock = 1 / dot_clock; /* duration of a dot */ 396 dot_clock = dot_clock / 2; /* dot_clock runs at twice */ 397 /* the dot rate */ 398 dot_clock = dot_clock * 100; /* scale for ioctl */ 399 400 cdot_clock = cpm / 2.4; /* dots/sec */ 401 cdot_clock = 1 / cdot_clock; /* duration of a dot */ 402 cdot_clock = cdot_clock / 2; /* dot_clock runs at twice */ 403 /* the dot rate */ 404 cdot_clock = cdot_clock * 100; /* scale for ioctl */ 405 } 406 407 argc -= optind; 408 argv += optind; 409 410 if (setlocale(LC_CTYPE, "") != NULL && 411 *(codeset = nl_langinfo(CODESET)) != '\0') { 412 if (strcmp(codeset, "KOI8-R") == 0) 413 hightab = koi8rtab; 414 else if (strcmp(codeset, "ISO8859-1") == 0 || 415 strcmp(codeset, "ISO8859-15") == 0) 416 hightab = iso8859_1tab; 417 else if (strcmp(codeset, "ISO8859-7") == 0) 418 hightab = iso8859_7tab; 419 } 420 421 if (lflag) 422 printf("m"); 423 if (*argv) { 424 do { 425 for (p = *argv; *p; ++p) { 426 if (eflag) 427 putchar(*p); 428 morse(*p); 429 } 430 if (eflag) 431 putchar(' '); 432 morse(' '); 433 } while (*++argv); 434 } else { 435 while ((ch = getchar()) != EOF) { 436 if (eflag) 437 putchar(ch); 438 morse(ch); 439 } 440 } 441 if (device) 442 tcsetattr(line, TCSANOW, &otty); 443 exit(0); 444} 445 446void 447morse(char c) 448{ 449 const struct morsetab *m; 450 451 if (isalpha((unsigned char)c)) 452 c = tolower((unsigned char)c); 453 if ((c == '\r') || (c == '\n')) 454 c = ' '; 455 if (c == ' ') { 456 if (pflag) 457 play(" "); 458 else if (device) 459 ttyout(" "); 460 else if (lflag) 461 printf("\n"); 462 else 463 show(""); 464 return; 465 } 466 for (m = ((unsigned char)c < 0x80? mtab: hightab); 467 m != NULL && m->inchar != '\0'; 468 m++) { 469 if (m->inchar == c) { 470 if (pflag) { 471 play(m->morse); 472 } else if (device) { 473 ttyout(m->morse); 474 } else 475 show(m->morse); 476 } 477 } 478} 479 480void 481show(const char *s) 482{ 483 if (lflag) { 484 printf("%s ", s); 485 } else if (sflag) { 486 printf(" %s\n", s); 487 } else { 488 for (; *s; ++s) 489 printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" : 490 "di" : "dah"); 491 printf("\n"); 492 } 493} 494 495void 496play(const char *s) 497{ 498#ifdef SPEAKER 499 const char *c; 500 501 for (c = s; *c != '\0'; c++) { 502 switch (*c) { 503 case '.': 504 sound.frequency = freq; 505 sound.duration = dot_clock; 506 break; 507 case '-': 508 sound.frequency = freq; 509 sound.duration = dot_clock * DASH_LEN; 510 break; 511 case ' ': 512 sound.frequency = 0; 513 sound.duration = cdot_clock * WORD_SPACE; 514 break; 515 default: 516 sound.duration = 0; 517 } 518 if (sound.duration) { 519 if (ioctl(spkr, SPKRTONE, &sound) == -1) { 520 perror("ioctl play"); 521 exit(1); 522 } 523 } 524 sound.frequency = 0; 525 sound.duration = dot_clock; 526 if (ioctl(spkr, SPKRTONE, &sound) == -1) { 527 perror("ioctl rest"); 528 exit(1); 529 } 530 } 531 sound.frequency = 0; 532 sound.duration = cdot_clock * CHAR_SPACE; 533 ioctl(spkr, SPKRTONE, &sound); 534#endif 535} 536 537void 538ttyout(const char *s) 539{ 540 const char *c; 541 int duration, on, lflags; 542 543 for (c = s; *c != '\0'; c++) { 544 switch (*c) { 545 case '.': 546 on = 1; 547 duration = dot_clock; 548 break; 549 case '-': 550 on = 1; 551 duration = dot_clock * DASH_LEN; 552 break; 553 case ' ': 554 on = 0; 555 duration = cdot_clock * WORD_SPACE; 556 break; 557 default: 558 on = 0; 559 duration = 0; 560 } 561 if (on) { 562 ioctl(line, TIOCMGET, &lflags); 563 lflags |= TIOCM_RTS; 564 ioctl(line, TIOCMSET, &lflags); 565 } 566 duration *= 10000; 567 if (duration) 568 usleep(duration); 569 ioctl(line, TIOCMGET, &lflags); 570 lflags &= ~TIOCM_RTS; 571 ioctl(line, TIOCMSET, &lflags); 572 duration = dot_clock * 10000; 573 usleep(duration); 574 } 575 duration = cdot_clock * CHAR_SPACE * 10000; 576 usleep(duration); 577} 578 579void 580sighandler(int signo) 581{ 582 583 ioctl(line, TIOCMSET, &olflags); 584 tcsetattr(line, TCSANOW, &otty); 585 586 signal(signo, SIG_DFL); 587 (void)kill(getpid(), signo); 588} 589