morse.c revision 203932
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#ifndef lint 36static const char copyright[] = 37"@(#) Copyright (c) 1988, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39#endif /* not lint */ 40 41#ifndef lint 42#if 0 43static char sccsid[] = "@(#)morse.c 8.1 (Berkeley) 5/31/93"; 44#endif 45static const char rcsid[] = 46 "$FreeBSD: head/games/morse/morse.c 203932 2010-02-15 18:46:02Z imp $"; 47#endif /* not lint */ 48 49#include <sys/time.h> 50 51#include <ctype.h> 52#include <fcntl.h> 53#include <langinfo.h> 54#include <locale.h> 55#include <signal.h> 56#include <stdio.h> 57#include <stdlib.h> 58#include <string.h> 59#include <termios.h> 60#include <unistd.h> 61 62/* Always use the speaker, let the open fail if -p is selected */ 63#define SPEAKER "/dev/speaker" 64 65#ifdef SPEAKER 66#include <dev/speaker/speaker.h> 67#endif 68 69struct morsetab { 70 const char inchar; 71 const char *morse; 72}; 73 74static const struct morsetab mtab[] = { 75 76 /* letters */ 77 78 {'a', ".-"}, 79 {'b', "-..."}, 80 {'c', "-.-."}, 81 {'d', "-.."}, 82 {'e', "."}, 83 {'f', "..-."}, 84 {'g', "--."}, 85 {'h', "...."}, 86 {'i', ".."}, 87 {'j', ".---"}, 88 {'k', "-.-"}, 89 {'l', ".-.."}, 90 {'m', "--"}, 91 {'n', "-."}, 92 {'o', "---"}, 93 {'p', ".--."}, 94 {'q', "--.-"}, 95 {'r', ".-."}, 96 {'s', "..."}, 97 {'t', "-"}, 98 {'u', "..-"}, 99 {'v', "...-"}, 100 {'w', ".--"}, 101 {'x', "-..-"}, 102 {'y', "-.--"}, 103 {'z', "--.."}, 104 105 /* digits */ 106 107 {'0', "-----"}, 108 {'1', ".----"}, 109 {'2', "..---"}, 110 {'3', "...--"}, 111 {'4', "....-"}, 112 {'5', "....."}, 113 {'6', "-...."}, 114 {'7', "--..."}, 115 {'8', "---.."}, 116 {'9', "----."}, 117 118 /* punctuation */ 119 120 {',', "--..--"}, 121 {'.', ".-.-.-"}, 122 {'"', ".-..-."}, 123 {'!', "..--."}, 124 {'?', "..--.."}, 125 {'/', "-..-."}, 126 {'-', "-....-"}, 127 {'=', "-...-"}, /* BT */ 128 {':', "---..."}, 129 {';', "-.-.-."}, 130 {'(', "-.--."}, /* KN */ 131 {')', "-.--.-"}, 132 {'$', "...-..-"}, 133 {'+', ".-.-."}, /* AR */ 134 {'@', ".--.-."}, /* AC */ 135 136 /* prosigns without already assigned values */ 137 138 {'#', ".-..."}, /* AS */ 139 {'&', "...-.-"}, /* SK */ 140 {'*', "...-."}, /* VE */ 141 {'%', "-...-.-"}, /* BK */ 142 143 {'\0', ""} 144}; 145 146 147static const struct morsetab iso8859_1tab[] = { 148 {'�', ".--.-"}, 149 {'�', ".--.-"}, 150 {'�', ".--.-"}, 151 {'�', ".-.-"}, 152 {'�', "-.-.."}, 153 {'�', "..-.."}, 154 {'�', "..-.."}, 155 {'�', "-..-."}, 156 {'�', "---."}, 157 {'�', "..--"}, 158 159 {'\0', ""} 160}; 161 162static const struct morsetab iso8859_7tab[] = { 163 /* 164 * The greek alphabet; you'll need an 8859-7 font in order 165 * to see the actual characters. 166 * This table does not implement: 167 * - the special sequences for the seven diphthongs, 168 * - the punctuation differences. 169 * Implementing these features would introduce too many 170 * special-cases in the program's main loop. 171 * The diphtong sequences are: 172 * alpha iota .-.- 173 * alpha upsilon ..-- 174 * epsilon upsilon ---. 175 * eta upsilon ...- 176 * omikron iota ---.. 177 * omikron upsilon ..- 178 * upsilon iota .--- 179 * The different punctuation symbols are: 180 * ; ..-.- 181 * ! --..-- 182 */ 183 {'�', ".-"}, /* alpha */ 184 {'�', ".-"}, /* alpha with acute */ 185 {'�', "-..."}, /* beta */ 186 {'�', "--."}, /* gamma */ 187 {'�', "-.."}, /* delta */ 188 {'�', "."}, /* epsilon */ 189 {'�', "."}, /* epsilon with acute */ 190 {'�', "--.."}, /* zeta */ 191 {'�', "...."}, /* eta */ 192 {'�', "...."}, /* eta with acute */ 193 {'�', "-.-."}, /* theta */ 194 {'�', ".."}, /* iota */ 195 {'�', ".."}, /* iota with acute */ 196 {'�', ".."}, /* iota with diairesis */ 197 {'�', ".."}, /* iota with acute and diairesis */ 198 {'�', "-.-"}, /* kappa */ 199 {'�', ".-.."}, /* lamda */ 200 {'�', "--"}, /* mu */ 201 {'�', "-."}, /* nu */ 202 {'�', "-..-"}, /* xi */ 203 {'�', "---"}, /* omicron */ 204 {'�', "---"}, /* omicron with acute */ 205 {'�', ".--."}, /* pi */ 206 {'�', ".-."}, /* rho */ 207 {'�', "..."}, /* sigma */ 208 {'�', "..."}, /* final sigma */ 209 {'�', "-"}, /* tau */ 210 {'�', "-.--"}, /* upsilon */ 211 {'�', "-.--"}, /* upsilon with acute */ 212 {'�', "-.--"}, /* upsilon and diairesis */ 213 {'�', "-.--"}, /* upsilon with acute and diairesis */ 214 {'�', "..-."}, /* phi */ 215 {'�', "----"}, /* chi */ 216 {'�', "--.-"}, /* psi */ 217 {'�', ".--"}, /* omega */ 218 {'�', ".--"}, /* omega with acute */ 219 220 {'\0', ""} 221}; 222 223static const struct morsetab koi8rtab[] = { 224 /* 225 * the cyrillic alphabet; you'll need a KOI8R font in order 226 * to see the actual characters 227 */ 228 {'�', ".-"}, /* a */ 229 {'�', "-..."}, /* be */ 230 {'�', ".--"}, /* ve */ 231 {'�', "--."}, /* ge */ 232 {'�', "-.."}, /* de */ 233 {'�', "."}, /* ye */ 234 {'�', "."}, /* yo, the same as ye */ 235 {'�', "...-"}, /* she */ 236 {'�', "--.."}, /* ze */ 237 {'�', ".."}, /* i */ 238 {'�', ".---"}, /* i kratkoye */ 239 {'�', "-.-"}, /* ka */ 240 {'�', ".-.."}, /* el */ 241 {'�', "--"}, /* em */ 242 {'�', "-."}, /* en */ 243 {'�', "---"}, /* o */ 244 {'�', ".--."}, /* pe */ 245 {'�', ".-."}, /* er */ 246 {'�', "..."}, /* es */ 247 {'�', "-"}, /* te */ 248 {'�', "..-"}, /* u */ 249 {'�', "..-."}, /* ef */ 250 {'�', "...."}, /* kha */ 251 {'�', "-.-."}, /* ce */ 252 {'�', "---."}, /* che */ 253 {'�', "----"}, /* sha */ 254 {'�', "--.-"}, /* shcha */ 255 {'�', "-.--"}, /* yi */ 256 {'�', "-..-"}, /* myakhkij znak */ 257 {'�', "..-.."}, /* ae */ 258 {'�', "..--"}, /* yu */ 259 {'�', ".-.-"}, /* ya */ 260 261 {'\0', ""} 262}; 263 264void show(const char *), play(const char *), morse(char); 265void ttyout(const char *); 266void sighandler(int); 267 268#define GETOPTOPTS "c:d:ef:lsw:" 269#define USAGE \ 270"usage: morse [-els] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n" 271 272static int pflag, lflag, sflag, eflag; 273static int wpm = 20; /* effective words per minute */ 274static int cpm; /* effective words per minute between 275 * characters */ 276#define FREQUENCY 600 277static int freq = FREQUENCY; 278static char *device; /* for tty-controlled generator */ 279 280#define DASH_LEN 3 281#define CHAR_SPACE 3 282#define WORD_SPACE (7 - CHAR_SPACE - 1) 283static float dot_clock; 284static float cdot_clock; 285int spkr, line; 286struct termios otty, ntty; 287int olflags; 288 289#ifdef SPEAKER 290tone_t sound; 291#undef GETOPTOPTS 292#define GETOPTOPTS "c:d:ef:lpsw:" 293#undef USAGE 294#define USAGE \ 295"usage: morse [-elps] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n" 296#endif 297 298static const struct morsetab *hightab; 299 300int 301main(int argc, char **argv) 302{ 303 int ch, lflags; 304 char *p, *codeset; 305 306 while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1) 307 switch ((char) ch) { 308 case 'c': 309 cpm = atoi(optarg); 310 break; 311 case 'd': 312 device = optarg; 313 break; 314 case 'e': 315 eflag = 1; 316 setvbuf(stdout, 0, _IONBF, 0); 317 break; 318 case 'f': 319 freq = atoi(optarg); 320 break; 321 case 'l': 322 lflag = 1; 323 break; 324#ifdef SPEAKER 325 case 'p': 326 pflag = 1; 327 break; 328#endif 329 case 's': 330 sflag = 1; 331 break; 332 case 'w': 333 wpm = atoi(optarg); 334 break; 335 case '?': 336 default: 337 fputs(USAGE, stderr); 338 exit(1); 339 } 340 if (sflag && lflag) { 341 fputs("morse: only one of -l and -s allowed\n", stderr); 342 exit(1); 343 } 344 if ((pflag || device) && (sflag || lflag)) { 345 fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr); 346 exit(1); 347 } 348 if (cpm == 0) 349 cpm = wpm; 350 if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) { 351 fputs("morse: insane speed\n", stderr); 352 exit(1); 353 } 354 if ((pflag || device) && (freq == 0)) 355 freq = FREQUENCY; 356 357#ifdef SPEAKER 358 if (pflag) { 359 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) { 360 perror(SPEAKER); 361 exit(1); 362 } 363 } else 364#endif 365 if (device) { 366 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) { 367 perror("open tty line"); 368 exit(1); 369 } 370 if (tcgetattr(line, &otty) == -1) { 371 perror("tcgetattr() failed"); 372 exit(1); 373 } 374 ntty = otty; 375 ntty.c_cflag |= CLOCAL; 376 tcsetattr(line, TCSANOW, &ntty); 377 lflags = fcntl(line, F_GETFL); 378 lflags &= ~O_NONBLOCK; 379 fcntl(line, F_SETFL, &lflags); 380 ioctl(line, TIOCMGET, &lflags); 381 lflags &= ~TIOCM_RTS; 382 olflags = lflags; 383 ioctl(line, TIOCMSET, &lflags); 384 (void)signal(SIGHUP, sighandler); 385 (void)signal(SIGINT, sighandler); 386 (void)signal(SIGQUIT, sighandler); 387 (void)signal(SIGTERM, sighandler); 388 } 389 if (pflag || device) { 390 dot_clock = wpm / 2.4; /* dots/sec */ 391 dot_clock = 1 / dot_clock; /* duration of a dot */ 392 dot_clock = dot_clock / 2; /* dot_clock runs at twice */ 393 /* the dot rate */ 394 dot_clock = dot_clock * 100; /* scale for ioctl */ 395 396 cdot_clock = cpm / 2.4; /* dots/sec */ 397 cdot_clock = 1 / cdot_clock; /* duration of a dot */ 398 cdot_clock = cdot_clock / 2; /* dot_clock runs at twice */ 399 /* the dot rate */ 400 cdot_clock = cdot_clock * 100; /* scale for ioctl */ 401 } 402 403 argc -= optind; 404 argv += optind; 405 406 if (setlocale(LC_CTYPE, "") != NULL && 407 *(codeset = nl_langinfo(CODESET)) != '\0') { 408 if (strcmp(codeset, "KOI8-R") == 0) 409 hightab = koi8rtab; 410 else if (strcmp(codeset, "ISO8859-1") == 0 || 411 strcmp(codeset, "ISO8859-15") == 0) 412 hightab = iso8859_1tab; 413 else if (strcmp(codeset, "ISO8859-7") == 0) 414 hightab = iso8859_7tab; 415 } 416 417 if (lflag) 418 printf("m"); 419 if (*argv) { 420 do { 421 for (p = *argv; *p; ++p) { 422 if (eflag) 423 putchar(*p); 424 morse(*p); 425 } 426 if (eflag) 427 putchar(' '); 428 morse(' '); 429 } while (*++argv); 430 } else { 431 while ((ch = getchar()) != EOF) { 432 if (eflag) 433 putchar(ch); 434 morse(ch); 435 } 436 } 437 if (device) 438 tcsetattr(line, TCSANOW, &otty); 439 exit(0); 440} 441 442void 443morse(char c) 444{ 445 const struct morsetab *m; 446 447 if (isalpha((unsigned char)c)) 448 c = tolower((unsigned char)c); 449 if ((c == '\r') || (c == '\n')) 450 c = ' '; 451 if (c == ' ') { 452 if (pflag) 453 play(" "); 454 else if (device) 455 ttyout(" "); 456 else if (lflag) 457 printf("\n"); 458 else 459 show(""); 460 return; 461 } 462 for (m = ((unsigned char)c < 0x80? mtab: hightab); 463 m != NULL && m->inchar != '\0'; 464 m++) { 465 if (m->inchar == c) { 466 if (pflag) { 467 play(m->morse); 468 } else if (device) { 469 ttyout(m->morse); 470 } else 471 show(m->morse); 472 } 473 } 474} 475 476void 477show(const char *s) 478{ 479 if (lflag) { 480 printf("%s ", s); 481 } else if (sflag) { 482 printf(" %s\n", s); 483 } else { 484 for (; *s; ++s) 485 printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" : 486 "di" : "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