morse.c revision 152306
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 (VE7TCP/VE6BBM) 36 * <lyndon@orthanc.com> 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 152306 2005-11-11 09:57:32Z ru $"; 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#ifdef SPEAKER 67#include <dev/speaker/speaker.h> 68#endif 69 70struct morsetab { 71 char inchar; 72 char *morse; 73}; 74 75static 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 /* prosigns without already assigned values */ 138 139 {'#', ".-..."}, /* AS */ 140 {'&', "...-.-"}, /* SK */ 141 {'*', "...-."}, /* VE */ 142 {'%', "-...-.-"}, /* BK */ 143 144 {'\0', ""} 145}; 146 147 148static const struct morsetab iso8859_1tab[] = { 149 {'�', ".--.-"}, 150 {'�', ".--.-"}, 151 {'�', ".--.-"}, 152 {'�', ".-.-"}, 153 {'�', "-.-.."}, 154 {'�', "..-.."}, 155 {'�', "..-.."}, 156 {'�', "-..-."}, 157 {'�', "---."}, 158 {'�', "..--"}, 159 160 {'\0', ""} 161}; 162 163static const struct morsetab iso8859_7tab[] = { 164 /* 165 * The greek alphabet; you'll need an 8859-7 font in order 166 * to see the actual characters. 167 * This table does not implement: 168 * - the special sequences for the seven diphthongs, 169 * - the punctuation differences. 170 * Implementing these features would introduce too many 171 * special-cases in the program's main loop. 172 * The diphtong sequences are: 173 * alpha iota .-.- 174 * alpha upsilon ..-- 175 * epsilon upsilon ---. 176 * eta upsilon ...- 177 * omikron iota ---.. 178 * omikron upsilon ..- 179 * upsilon iota .--- 180 * The different punctuation symbols are: 181 * ; ..-.- 182 * ! --..-- 183 */ 184 {'�', ".-"}, /* alpha */ 185 {'�', ".-"}, /* alpha with acute */ 186 {'�', "-..."}, /* beta */ 187 {'�', "--."}, /* gamma */ 188 {'�', "-.."}, /* delta */ 189 {'�', "."}, /* epsilon */ 190 {'�', "."}, /* epsilon with acute */ 191 {'�', "--.."}, /* zeta */ 192 {'�', "...."}, /* eta */ 193 {'�', "...."}, /* eta with acute */ 194 {'�', "-.-."}, /* theta */ 195 {'�', ".."}, /* iota */ 196 {'�', ".."}, /* iota with acute */ 197 {'�', ".."}, /* iota with diairesis */ 198 {'�', ".."}, /* iota with acute and diairesis */ 199 {'�', "-.-"}, /* kappa */ 200 {'�', ".-.."}, /* lamda */ 201 {'�', "--"}, /* mu */ 202 {'�', "-."}, /* nu */ 203 {'�', "-..-"}, /* xi */ 204 {'�', "---"}, /* omicron */ 205 {'�', "---"}, /* omicron with acute */ 206 {'�', ".--."}, /* pi */ 207 {'�', ".-."}, /* rho */ 208 {'�', "..."}, /* sigma */ 209 {'�', "..."}, /* final sigma */ 210 {'�', "-"}, /* tau */ 211 {'�', "-.--"}, /* upsilon */ 212 {'�', "-.--"}, /* upsilon with acute */ 213 {'�', "-.--"}, /* upsilon and diairesis */ 214 {'�', "-.--"}, /* upsilon with acute and diairesis */ 215 {'�', "..-."}, /* phi */ 216 {'�', "----"}, /* chi */ 217 {'�', "--.-"}, /* psi */ 218 {'�', ".--"}, /* omega */ 219 {'�', ".--"}, /* omega with acute */ 220 221 {'\0', ""} 222}; 223 224static const struct morsetab koi8rtab[] = { 225 /* 226 * the cyrillic alphabet; you'll need a KOI8R font in order 227 * to see the actual characters 228 */ 229 {'�', ".-"}, /* a */ 230 {'�', "-..."}, /* be */ 231 {'�', ".--"}, /* ve */ 232 {'�', "--."}, /* ge */ 233 {'�', "-.."}, /* de */ 234 {'�', "."}, /* ye */ 235 {'�', "."}, /* yo, the same as ye */ 236 {'�', "...-"}, /* she */ 237 {'�', "--.."}, /* ze */ 238 {'�', ".."}, /* i */ 239 {'�', ".---"}, /* i kratkoye */ 240 {'�', "-.-"}, /* ka */ 241 {'�', ".-.."}, /* el */ 242 {'�', "--"}, /* em */ 243 {'�', "-."}, /* en */ 244 {'�', "---"}, /* o */ 245 {'�', ".--."}, /* pe */ 246 {'�', ".-."}, /* er */ 247 {'�', "..."}, /* es */ 248 {'�', "-"}, /* te */ 249 {'�', "..-"}, /* u */ 250 {'�', "..-."}, /* ef */ 251 {'�', "...."}, /* kha */ 252 {'�', "-.-."}, /* ce */ 253 {'�', "---."}, /* che */ 254 {'�', "----"}, /* sha */ 255 {'�', "--.-"}, /* shcha */ 256 {'�', "-.--"}, /* yi */ 257 {'�', "-..-"}, /* myakhkij znak */ 258 {'�', "..-.."}, /* ae */ 259 {'�', "..--"}, /* yu */ 260 {'�', ".-.-"}, /* ya */ 261 262 {'\0', ""} 263}; 264 265void show(const char *), play(const char *), morse(char); 266void ttyout(const char *); 267void sighandler(int); 268 269#define GETOPTOPTS "c:d:ef:lsw:" 270#define USAGE \ 271"usage: morse [-els] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n" 272 273static int pflag, lflag, sflag, eflag; 274static int wpm = 20; /* effective words per minute */ 275static int cpm; /* effective words per minute between 276 * characters */ 277#define FREQUENCY 600 278static int freq = FREQUENCY; 279static char *device; /* for tty-controlled generator */ 280 281#define DASH_LEN 3 282#define CHAR_SPACE 3 283#define WORD_SPACE (7 - CHAR_SPACE - 1) 284static float dot_clock; 285static float cdot_clock; 286int spkr, line; 287struct termios otty, ntty; 288int olflags; 289 290#ifdef SPEAKER 291tone_t sound; 292#undef GETOPTOPTS 293#define GETOPTOPTS "c:d:ef:lpsw:" 294#undef USAGE 295#define USAGE \ 296"usage: morse [-elps] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n" 297#endif 298 299static const struct morsetab *hightab; 300 301int 302main(int argc, char **argv) 303{ 304 int ch, lflags; 305 char *p, *codeset; 306 307 while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1) 308 switch ((char) ch) { 309 case 'c': 310 cpm = atoi(optarg); 311 break; 312 case 'd': 313 device = optarg; 314 break; 315 case 'e': 316 eflag = 1; 317 setvbuf(stdout, 0, _IONBF, 0); 318 break; 319 case 'f': 320 freq = atoi(optarg); 321 break; 322 case 'l': 323 lflag = 1; 324 break; 325#ifdef SPEAKER 326 case 'p': 327 pflag = 1; 328 break; 329#endif 330 case 's': 331 sflag = 1; 332 break; 333 case 'w': 334 wpm = atoi(optarg); 335 break; 336 case '?': 337 default: 338 fputs(USAGE, stderr); 339 exit(1); 340 } 341 if (sflag && lflag) { 342 fputs("morse: only one of -l and -s allowed\n", stderr); 343 exit(1); 344 } 345 if ((pflag || device) && (sflag || lflag)) { 346 fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr); 347 exit(1); 348 } 349 if (cpm == 0) 350 cpm = wpm; 351 if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) { 352 fputs("morse: insane speed\n", stderr); 353 exit(1); 354 } 355 if ((pflag || device) && (freq == 0)) 356 freq = FREQUENCY; 357 358#ifdef SPEAKER 359 if (pflag) { 360 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) { 361 perror(SPEAKER); 362 exit(1); 363 } 364 } else 365#endif 366 if (device) { 367 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) { 368 perror("open tty line"); 369 exit(1); 370 } 371 if (tcgetattr(line, &otty) == -1) { 372 perror("tcgetattr() failed"); 373 exit(1); 374 } 375 ntty = otty; 376 ntty.c_cflag |= CLOCAL; 377 tcsetattr(line, TCSANOW, &ntty); 378 lflags = fcntl(line, F_GETFL); 379 lflags &= ~O_NONBLOCK; 380 fcntl(line, F_SETFL, &lflags); 381 ioctl(line, TIOCMGET, &lflags); 382 lflags &= ~TIOCM_RTS; 383 olflags = lflags; 384 ioctl(line, TIOCMSET, &lflags); 385 (void)signal(SIGHUP, sighandler); 386 (void)signal(SIGINT, sighandler); 387 (void)signal(SIGQUIT, sighandler); 388 (void)signal(SIGTERM, sighandler); 389 } 390 if (pflag || device) { 391 dot_clock = wpm / 2.4; /* dots/sec */ 392 dot_clock = 1 / dot_clock; /* duration of a dot */ 393 dot_clock = dot_clock / 2; /* dot_clock runs at twice */ 394 /* the dot rate */ 395 dot_clock = dot_clock * 100; /* scale for ioctl */ 396 397 cdot_clock = cpm / 2.4; /* dots/sec */ 398 cdot_clock = 1 / cdot_clock; /* duration of a dot */ 399 cdot_clock = cdot_clock / 2; /* dot_clock runs at twice */ 400 /* the dot rate */ 401 cdot_clock = cdot_clock * 100; /* scale for ioctl */ 402 } 403 404 argc -= optind; 405 argv += optind; 406 407 if (setlocale(LC_CTYPE, "") != NULL && 408 *(codeset = nl_langinfo(CODESET)) != '\0') { 409 if (strcmp(codeset, "KOI8-R") == 0) 410 hightab = koi8rtab; 411 else if (strcmp(codeset, "ISO8859-1") == 0 || 412 strcmp(codeset, "ISO8859-15") == 0) 413 hightab = iso8859_1tab; 414 else if (strcmp(codeset, "ISO8859-7") == 0) 415 hightab = iso8859_7tab; 416 } 417 418 if (lflag) 419 printf("m"); 420 if (*argv) { 421 do { 422 for (p = *argv; *p; ++p) { 423 if (eflag) 424 putchar(*p); 425 morse(*p); 426 } 427 if (eflag) 428 putchar(' '); 429 morse(' '); 430 } while (*++argv); 431 } else { 432 while ((ch = getchar()) != EOF) { 433 if (eflag) 434 putchar(ch); 435 morse(ch); 436 } 437 } 438 if (device) 439 tcsetattr(line, TCSANOW, &otty); 440 exit(0); 441} 442 443void 444morse(char c) 445{ 446 const struct morsetab *m; 447 448 if (isalpha((unsigned char)c)) 449 c = tolower((unsigned char)c); 450 if ((c == '\r') || (c == '\n')) 451 c = ' '; 452 if (c == ' ') { 453 if (pflag) 454 play(" "); 455 else if (device) 456 ttyout(" "); 457 else if (lflag) 458 printf("\n"); 459 else 460 show(""); 461 return; 462 } 463 for (m = ((unsigned char)c < 0x80? mtab: hightab); 464 m != NULL && m->inchar != '\0'; 465 m++) { 466 if (m->inchar == c) { 467 if (pflag) { 468 play(m->morse); 469 } else if (device) { 470 ttyout(m->morse); 471 } else 472 show(m->morse); 473 } 474 } 475} 476 477void 478show(const char *s) 479{ 480 if (lflag) { 481 printf("%s ", s); 482 } else if (sflag) { 483 printf(" %s\n", s); 484 } else { 485 for (; *s; ++s) 486 printf(" %s", *s == '.' ? "dit" : "dah"); 487 printf("\n"); 488 } 489} 490 491void 492play(const char *s) 493{ 494#ifdef SPEAKER 495 const char *c; 496 497 for (c = s; *c != '\0'; c++) { 498 switch (*c) { 499 case '.': 500 sound.frequency = freq; 501 sound.duration = dot_clock; 502 break; 503 case '-': 504 sound.frequency = freq; 505 sound.duration = dot_clock * DASH_LEN; 506 break; 507 case ' ': 508 sound.frequency = 0; 509 sound.duration = cdot_clock * WORD_SPACE; 510 break; 511 default: 512 sound.duration = 0; 513 } 514 if (sound.duration) { 515 if (ioctl(spkr, SPKRTONE, &sound) == -1) { 516 perror("ioctl play"); 517 exit(1); 518 } 519 } 520 sound.frequency = 0; 521 sound.duration = dot_clock; 522 if (ioctl(spkr, SPKRTONE, &sound) == -1) { 523 perror("ioctl rest"); 524 exit(1); 525 } 526 } 527 sound.frequency = 0; 528 sound.duration = cdot_clock * CHAR_SPACE; 529 ioctl(spkr, SPKRTONE, &sound); 530#endif 531} 532 533void 534ttyout(const char *s) 535{ 536 const char *c; 537 int duration, on, lflags; 538 539 for (c = s; *c != '\0'; c++) { 540 switch (*c) { 541 case '.': 542 on = 1; 543 duration = dot_clock; 544 break; 545 case '-': 546 on = 1; 547 duration = dot_clock * DASH_LEN; 548 break; 549 case ' ': 550 on = 0; 551 duration = cdot_clock * WORD_SPACE; 552 break; 553 default: 554 on = 0; 555 duration = 0; 556 } 557 if (on) { 558 ioctl(line, TIOCMGET, &lflags); 559 lflags |= TIOCM_RTS; 560 ioctl(line, TIOCMSET, &lflags); 561 } 562 duration *= 10000; 563 if (duration) 564 usleep(duration); 565 ioctl(line, TIOCMGET, &lflags); 566 lflags &= ~TIOCM_RTS; 567 ioctl(line, TIOCMSET, &lflags); 568 duration = dot_clock * 10000; 569 usleep(duration); 570 } 571 duration = cdot_clock * CHAR_SPACE * 10000; 572 usleep(duration); 573} 574 575void 576sighandler(int signo) 577{ 578 579 ioctl(line, TIOCMSET, &olflags); 580 tcsetattr(line, TCSANOW, &otty); 581 582 signal(signo, SIG_DFL); 583 (void)kill(getpid(), signo); 584} 585