morse.c revision 129114
1293531Sdchagin/* 2293531Sdchagin * Copyright (c) 1988, 1993 3293531Sdchagin * The Regents of the University of California. All rights reserved. 4293531Sdchagin * 5293531Sdchagin * Redistribution and use in source and binary forms, with or without 6294369Sjhb * modification, are permitted provided that the following conditions 7293531Sdchagin * are met: 8293531Sdchagin * 1. Redistributions of source code must retain the above copyright 9293531Sdchagin * notice, this list of conditions and the following disclaimer. 10293531Sdchagin * 2. Redistributions in binary form must reproduce the above copyright 11293531Sdchagin * notice, this list of conditions and the following disclaimer in the 12293531Sdchagin * documentation and/or other materials provided with the distribution. 13293531Sdchagin * 3. All advertising materials mentioning features or use of this software 14293531Sdchagin * must display the following acknowledgement: 15293531Sdchagin * This product includes software developed by the University of 16293531Sdchagin * California, Berkeley and its contributors. 17293531Sdchagin * 4. Neither the name of the University nor the names of its contributors 18293531Sdchagin * may be used to endorse or promote products derived from this software 19293531Sdchagin * without specific prior written permission. 20293531Sdchagin * 21293531Sdchagin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22293531Sdchagin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23293531Sdchagin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24293531Sdchagin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25293531Sdchagin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26293531Sdchagin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27293531Sdchagin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28293531Sdchagin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29293531Sdchagin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30293531Sdchagin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31293531Sdchagin * SUCH DAMAGE. 32293531Sdchagin */ 33293531Sdchagin 34293531Sdchagin/* 35293531Sdchagin * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM) 36293531Sdchagin * <lyndon@orthanc.com> 37293531Sdchagin */ 38293531Sdchagin 39293531Sdchagin#ifndef lint 40293531Sdchaginstatic const char copyright[] = 41293531Sdchagin"@(#) Copyright (c) 1988, 1993\n\ 42293531Sdchagin The Regents of the University of California. All rights reserved.\n"; 43293531Sdchagin#endif /* not lint */ 44293531Sdchagin 45293531Sdchagin#ifndef lint 46293531Sdchagin#if 0 47293531Sdchaginstatic char sccsid[] = "@(#)morse.c 8.1 (Berkeley) 5/31/93"; 48293531Sdchagin#endif 49293531Sdchaginstatic const char rcsid[] = 50293531Sdchagin "$FreeBSD: head/games/morse/morse.c 129114 2004-05-11 11:11:14Z dds $"; 51293531Sdchagin#endif /* not lint */ 52293531Sdchagin 53293531Sdchagin#include <sys/time.h> 54293531Sdchagin 55293531Sdchagin#include <ctype.h> 56293531Sdchagin#include <fcntl.h> 57293531Sdchagin#include <langinfo.h> 58293531Sdchagin#include <locale.h> 59293531Sdchagin#include <signal.h> 60293531Sdchagin#include <stdio.h> 61293531Sdchagin#include <stdlib.h> 62293531Sdchagin#include <string.h> 63293531Sdchagin#include <termios.h> 64293531Sdchagin#include <unistd.h> 65293531Sdchagin 66293531Sdchagin#ifdef SPEAKER 67293531Sdchagin#include <machine/speaker.h> 68293531Sdchagin#endif 69293531Sdchagin 70293531Sdchaginstruct morsetab { 71293531Sdchagin char inchar; 72293531Sdchagin char *morse; 73293531Sdchagin}; 74293531Sdchagin 75293531Sdchaginstatic const struct morsetab mtab[] = { 76293531Sdchagin 77293531Sdchagin /* letters */ 78293531Sdchagin 79293531Sdchagin {'a', ".-"}, 80293531Sdchagin {'b', "-..."}, 81293531Sdchagin {'c', "-.-."}, 82293531Sdchagin {'d', "-.."}, 83293531Sdchagin {'e', "."}, 84293531Sdchagin {'f', "..-."}, 85293531Sdchagin {'g', "--."}, 86293531Sdchagin {'h', "...."}, 87293531Sdchagin {'i', ".."}, 88293531Sdchagin {'j', ".---"}, 89293531Sdchagin {'k', "-.-"}, 90293531Sdchagin {'l', ".-.."}, 91293531Sdchagin {'m', "--"}, 92293531Sdchagin {'n', "-."}, 93293531Sdchagin {'o', "---"}, 94293531Sdchagin {'p', ".--."}, 95293531Sdchagin {'q', "--.-"}, 96293531Sdchagin {'r', ".-."}, 97293531Sdchagin {'s', "..."}, 98293531Sdchagin {'t', "-"}, 99293531Sdchagin {'u', "..-"}, 100293531Sdchagin {'v', "...-"}, 101293531Sdchagin {'w', ".--"}, 102293531Sdchagin {'x', "-..-"}, 103293531Sdchagin {'y', "-.--"}, 104293531Sdchagin {'z', "--.."}, 105293531Sdchagin 106293531Sdchagin /* digits */ 107293531Sdchagin 108293531Sdchagin {'0', "-----"}, 109293531Sdchagin {'1', ".----"}, 110293531Sdchagin {'2', "..---"}, 111293531Sdchagin {'3', "...--"}, 112293531Sdchagin {'4', "....-"}, 113293531Sdchagin {'5', "....."}, 114293531Sdchagin {'6', "-...."}, 115293531Sdchagin {'7', "--..."}, 116293531Sdchagin {'8', "---.."}, 117293531Sdchagin {'9', "----."}, 118293531Sdchagin 119293531Sdchagin /* punctuation */ 120293531Sdchagin 121293531Sdchagin {',', "--..--"}, 122293531Sdchagin {'.', ".-.-.-"}, 123293531Sdchagin {'"', ".-..-."}, 124293531Sdchagin {'!', "..--."}, 125293531Sdchagin {'?', "..--.."}, 126293531Sdchagin {'/', "-..-."}, 127293531Sdchagin {'-', "-....-"}, 128293531Sdchagin {'=', "-...-"}, /* BT */ 129293531Sdchagin {':', "---..."}, 130293531Sdchagin {';', "-.-.-."}, 131293531Sdchagin {'(', "-.--."}, /* KN */ 132293531Sdchagin {')', "-.--.-"}, 133293531Sdchagin {'$', "...-..-"}, 134293531Sdchagin {'+', ".-.-."}, /* AR */ 135293531Sdchagin {'@', ".--.-."}, /* AC */ 136293531Sdchagin 137293531Sdchagin /* prosigns without already assigned values */ 138293531Sdchagin 139293531Sdchagin {'#', ".-..."}, /* AS */ 140293531Sdchagin {'&', "...-.-"}, /* SK */ 141293531Sdchagin {'*', "...-."}, /* VE */ 142293531Sdchagin {'%', "-...-.-"}, /* BK */ 143293531Sdchagin 144293531Sdchagin {'\0', ""} 145293531Sdchagin}; 146293531Sdchagin 147293531Sdchagin 148293531Sdchaginstatic const struct morsetab iso8859_1tab[] = { 149293531Sdchagin {'�', ".--.-"}, 150293531Sdchagin {'�', ".--.-"}, 151293531Sdchagin {'�', ".--.-"}, 152293531Sdchagin {'�', ".-.-"}, 153293531Sdchagin {'�', "-.-.."}, 154293531Sdchagin {'�', "..-.."}, 155293531Sdchagin {'�', "..-.."}, 156293531Sdchagin {'�', "-..-."}, 157293531Sdchagin {'�', "---."}, 158293531Sdchagin {'�', "..--"}, 159293531Sdchagin 160293531Sdchagin {'\0', ""} 161293531Sdchagin}; 162293531Sdchagin 163293531Sdchaginstatic const struct morsetab iso8859_7tab[] = { 164293531Sdchagin /* 165293531Sdchagin * The greek alphabet; you'll need an 8859-7 font in order 166293531Sdchagin * to see the actual characters. 167293531Sdchagin * This table does not implement: 168293531Sdchagin * - the special sequences for the seven diphthongs, 169293531Sdchagin * - the punctuation differences. 170293531Sdchagin * Implementing these features would introduce too many 171293531Sdchagin * special-cases in the program's main loop. 172293531Sdchagin * The diphtong sequences are: 173293531Sdchagin * alpha iota .-.- 174293531Sdchagin * alpha upsilon ..-- 175293531Sdchagin * epsilon upsilon ---. 176293531Sdchagin * eta upsilon ...- 177293531Sdchagin * omikron iota ---.. 178293531Sdchagin * omikron upsilon ..- 179293531Sdchagin * upsilon iota .--- 180293531Sdchagin * The different punctuation symbols are: 181293531Sdchagin * ; ..-.- 182293531Sdchagin * ! --..-- 183293531Sdchagin */ 184293531Sdchagin {'�', ".-"}, /* alpha */ 185293531Sdchagin {'�', ".-"}, /* alpha with acute */ 186293531Sdchagin {'�', "-..."}, /* beta */ 187293531Sdchagin {'�', "--."}, /* gamma */ 188293531Sdchagin {'�', "-.."}, /* delta */ 189293531Sdchagin {'�', "."}, /* epsilon */ 190293531Sdchagin {'�', "."}, /* epsilon with acute */ 191293531Sdchagin {'�', "--.."}, /* zeta */ 192293531Sdchagin {'�', "...."}, /* eta */ 193293531Sdchagin {'�', "...."}, /* eta with acute */ 194293531Sdchagin {'�', "-.-."}, /* theta */ 195293531Sdchagin {'�', ".."}, /* iota */ 196293531Sdchagin {'�', ".."}, /* iota with acute */ 197293531Sdchagin {'�', ".."}, /* iota with diairesis */ 198293531Sdchagin {'�', ".."}, /* iota with acute and diairesis */ 199293531Sdchagin {'�', "-.-"}, /* kappa */ 200293531Sdchagin {'�', ".-.."}, /* lamda */ 201293531Sdchagin {'�', "--"}, /* mu */ 202293531Sdchagin {'�', "-."}, /* nu */ 203293531Sdchagin {'�', "-..-"}, /* xi */ 204293531Sdchagin {'�', "---"}, /* omicron */ 205293531Sdchagin {'�', "---"}, /* omicron with acute */ 206293531Sdchagin {'�', ".--."}, /* pi */ 207293531Sdchagin {'�', ".-."}, /* rho */ 208293531Sdchagin {'�', "..."}, /* sigma */ 209293531Sdchagin {'�', "..."}, /* final sigma */ 210293531Sdchagin {'�', "-"}, /* tau */ 211293531Sdchagin {'�', "-.--"}, /* upsilon */ 212293531Sdchagin {'�', "-.--"}, /* upsilon with acute */ 213293531Sdchagin {'�', "-.--"}, /* upsilon and diairesis */ 214293531Sdchagin {'�', "-.--"}, /* upsilon with acute and diairesis */ 215293531Sdchagin {'�', "..-."}, /* phi */ 216293531Sdchagin {'�', "----"}, /* chi */ 217293531Sdchagin {'�', "--.-"}, /* psi */ 218293531Sdchagin {'�', ".--"}, /* omega */ 219293531Sdchagin {'�', ".--"}, /* omega with acute */ 220293531Sdchagin 221293531Sdchagin {'\0', ""} 222293531Sdchagin}; 223293531Sdchagin 224293531Sdchaginstatic const struct morsetab koi8rtab[] = { 225293531Sdchagin /* 226293531Sdchagin * the cyrillic alphabet; you'll need a KOI8R font in order 227293531Sdchagin * to see the actual characters 228293531Sdchagin */ 229293531Sdchagin {'�', ".-"}, /* a */ 230293531Sdchagin {'�', "-..."}, /* be */ 231293531Sdchagin {'�', ".--"}, /* ve */ 232293531Sdchagin {'�', "--."}, /* ge */ 233293531Sdchagin {'�', "-.."}, /* de */ 234293531Sdchagin {'�', "."}, /* ye */ 235293531Sdchagin {'�', "."}, /* yo, the same as ye */ 236293531Sdchagin {'�', "...-"}, /* she */ 237293531Sdchagin {'�', "--.."}, /* ze */ 238293531Sdchagin {'�', ".."}, /* i */ 239293531Sdchagin {'�', ".---"}, /* i kratkoye */ 240293531Sdchagin {'�', "-.-"}, /* ka */ 241293531Sdchagin {'�', ".-.."}, /* el */ 242293531Sdchagin {'�', "--"}, /* em */ 243293531Sdchagin {'�', "-."}, /* en */ 244293531Sdchagin {'�', "---"}, /* o */ 245293531Sdchagin {'�', ".--."}, /* pe */ 246293531Sdchagin {'�', ".-."}, /* er */ 247293531Sdchagin {'�', "..."}, /* es */ 248293531Sdchagin {'�', "-"}, /* te */ 249293531Sdchagin {'�', "..-"}, /* u */ 250293531Sdchagin {'�', "..-."}, /* ef */ 251293531Sdchagin {'�', "...."}, /* kha */ 252293531Sdchagin {'�', "-.-."}, /* ce */ 253293531Sdchagin {'�', "---."}, /* che */ 254293531Sdchagin {'�', "----"}, /* sha */ 255293531Sdchagin {'�', "--.-"}, /* shcha */ 256293531Sdchagin {'�', "-.--"}, /* yi */ 257293531Sdchagin {'�', "-..-"}, /* myakhkij znak */ 258293531Sdchagin {'�', "..-.."}, /* ae */ 259293531Sdchagin {'�', "..--"}, /* yu */ 260293531Sdchagin {'�', ".-.-"}, /* ya */ 261293531Sdchagin 262293531Sdchagin {'\0', ""} 263293531Sdchagin}; 264293531Sdchagin 265293531Sdchaginvoid show(const char *), play(const char *), morse(char); 266293531Sdchaginvoid ttyout(const char *); 267293531Sdchaginvoid sighandler(int); 268293531Sdchagin 269293531Sdchagin#define GETOPTOPTS "d:ef:lsw:" 270293531Sdchagin#define USAGE \ 271293531Sdchagin"usage: morse [-els] [-d device] [-w speed] [-f frequency] [string ...]\n" 272293531Sdchagin 273293531Sdchaginstatic int pflag, lflag, sflag, eflag; 274293531Sdchaginstatic int wpm = 20; /* words per minute */ 275293531Sdchagin#define FREQUENCY 600 276293531Sdchaginstatic int freq = FREQUENCY; 277293531Sdchaginstatic char *device; /* for tty-controlled generator */ 278293531Sdchagin 279293531Sdchagin#define DASH_LEN 3 280293531Sdchagin#define CHAR_SPACE 3 281293531Sdchagin#define WORD_SPACE (7 - CHAR_SPACE - 1) 282293531Sdchaginstatic float dot_clock; 283293531Sdchaginint spkr, line; 284293531Sdchaginstruct termios otty, ntty; 285293531Sdchaginint olflags; 286293531Sdchagin 287293531Sdchagin#ifdef SPEAKER 288293531Sdchagintone_t sound; 289293531Sdchagin#undef GETOPTOPTS 290293531Sdchagin#define GETOPTOPTS "d:ef:lpsw:" 291293531Sdchagin#undef USAGE 292293531Sdchagin#define USAGE \ 293293531Sdchagin"usage: morse [-s] [-p] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n" 294293531Sdchagin#endif 295293531Sdchagin 296293531Sdchaginstatic const struct morsetab *hightab; 297293531Sdchagin 298293531Sdchaginint 299293531Sdchaginmain(int argc, char **argv) 300293531Sdchagin{ 301293531Sdchagin int ch, lflags; 302293531Sdchagin char *p, *codeset; 303293531Sdchagin 304293531Sdchagin while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1) 305293531Sdchagin switch ((char) ch) { 306293531Sdchagin case 'd': 307293531Sdchagin device = optarg; 308293531Sdchagin break; 309293531Sdchagin case 'e': 310293570Sdchagin eflag = 1; 311 setvbuf(stdout, 0, _IONBF, 0); 312 break; 313 case 'f': 314 freq = atoi(optarg); 315 break; 316 case 'l': 317 lflag = 1; 318 break; 319#ifdef SPEAKER 320 case 'p': 321 pflag = 1; 322 break; 323#endif 324 case 's': 325 sflag = 1; 326 break; 327 case 'w': 328 wpm = atoi(optarg); 329 break; 330 case '?': 331 default: 332 fputs(USAGE, stderr); 333 exit(1); 334 } 335 if (sflag && lflag) { 336 fputs("morse: only one of -l and -s allowed\n", stderr); 337 exit(1); 338 } 339 if ((pflag || device) && (sflag || lflag)) { 340 fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr); 341 exit(1); 342 } 343 if ((pflag || device) && ((wpm < 1) || (wpm > 60))) { 344 fputs("morse: insane speed\n", stderr); 345 exit(1); 346 } 347 if ((pflag || device) && (freq == 0)) 348 freq = FREQUENCY; 349 350#ifdef SPEAKER 351 if (pflag) { 352 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) { 353 perror(SPEAKER); 354 exit(1); 355 } 356 } else 357#endif 358 if (device) { 359 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) { 360 perror("open tty line"); 361 exit(1); 362 } 363 if (tcgetattr(line, &otty) == -1) { 364 perror("tcgetattr() failed"); 365 exit(1); 366 } 367 ntty = otty; 368 ntty.c_cflag |= CLOCAL; 369 tcsetattr(line, TCSANOW, &ntty); 370 lflags = fcntl(line, F_GETFL); 371 lflags &= ~O_NONBLOCK; 372 fcntl(line, F_SETFL, &lflags); 373 ioctl(line, TIOCMGET, &lflags); 374 lflags &= ~TIOCM_RTS; 375 olflags = lflags; 376 ioctl(line, TIOCMSET, &lflags); 377 (void)signal(SIGHUP, sighandler); 378 (void)signal(SIGINT, sighandler); 379 (void)signal(SIGQUIT, sighandler); 380 (void)signal(SIGTERM, sighandler); 381 } 382 if (pflag || device) { 383 dot_clock = wpm / 2.4; /* dots/sec */ 384 dot_clock = 1 / dot_clock; /* duration of a dot */ 385 dot_clock = dot_clock / 2; /* dot_clock runs at twice */ 386 /* the dot rate */ 387 dot_clock = dot_clock * 100; /* scale for ioctl */ 388 } 389 390 argc -= optind; 391 argv += optind; 392 393 if (setlocale(LC_CTYPE, "") != NULL && 394 *(codeset = nl_langinfo(CODESET)) != '\0') { 395 if (strcmp(codeset, "KOI8-R") == 0) 396 hightab = koi8rtab; 397 else if (strcmp(codeset, "ISO8859-1") == 0 || 398 strcmp(codeset, "ISO8859-15") == 0) 399 hightab = iso8859_1tab; 400 else if (strcmp(codeset, "ISO8859-7") == 0) 401 hightab = iso8859_7tab; 402 } 403 404 if (lflag) 405 printf("m"); 406 if (*argv) { 407 do { 408 for (p = *argv; *p; ++p) { 409 if (eflag) 410 putchar(*p); 411 morse(*p); 412 } 413 if (eflag) 414 putchar(' '); 415 morse(' '); 416 } while (*++argv); 417 } else { 418 while ((ch = getchar()) != EOF) { 419 if (eflag) 420 putchar(ch); 421 morse(ch); 422 } 423 } 424 if (device) 425 tcsetattr(line, TCSANOW, &otty); 426 exit(0); 427} 428 429void 430morse(char c) 431{ 432 const struct morsetab *m; 433 434 if (isalpha((unsigned char)c)) 435 c = tolower((unsigned char)c); 436 if ((c == '\r') || (c == '\n')) 437 c = ' '; 438 if (c == ' ') { 439 if (pflag) 440 play(" "); 441 else if (device) 442 ttyout(" "); 443 else if (lflag) 444 printf("\n"); 445 else 446 show(""); 447 return; 448 } 449 for (m = ((unsigned char)c < 0x80? mtab: hightab); 450 m != NULL && m->inchar != '\0'; 451 m++) { 452 if (m->inchar == c) { 453 if (pflag) { 454 play(m->morse); 455 } else if (device) { 456 ttyout(m->morse); 457 } else 458 show(m->morse); 459 } 460 } 461} 462 463void 464show(const char *s) 465{ 466 if (lflag) { 467 printf("%s ", s); 468 } else if (sflag) { 469 printf(" %s\n", s); 470 } else { 471 for (; *s; ++s) 472 printf(" %s", *s == '.' ? "dit" : "dah"); 473 printf("\n"); 474 } 475} 476 477void 478play(const char *s) 479{ 480#ifdef SPEAKER 481 const char *c; 482 483 for (c = s; *c != '\0'; c++) { 484 switch (*c) { 485 case '.': 486 sound.frequency = freq; 487 sound.duration = dot_clock; 488 break; 489 case '-': 490 sound.frequency = freq; 491 sound.duration = dot_clock * DASH_LEN; 492 break; 493 case ' ': 494 sound.frequency = 0; 495 sound.duration = dot_clock * WORD_SPACE; 496 break; 497 default: 498 sound.duration = 0; 499 } 500 if (sound.duration) { 501 if (ioctl(spkr, SPKRTONE, &sound) == -1) { 502 perror("ioctl play"); 503 exit(1); 504 } 505 } 506 sound.frequency = 0; 507 sound.duration = dot_clock; 508 if (ioctl(spkr, SPKRTONE, &sound) == -1) { 509 perror("ioctl rest"); 510 exit(1); 511 } 512 } 513 sound.frequency = 0; 514 sound.duration = dot_clock * CHAR_SPACE; 515 ioctl(spkr, SPKRTONE, &sound); 516#endif 517} 518 519void 520ttyout(const char *s) 521{ 522 const char *c; 523 int duration, on, lflags; 524 525 for (c = s; *c != '\0'; c++) { 526 switch (*c) { 527 case '.': 528 on = 1; 529 duration = dot_clock; 530 break; 531 case '-': 532 on = 1; 533 duration = dot_clock * DASH_LEN; 534 break; 535 case ' ': 536 on = 0; 537 duration = dot_clock * WORD_SPACE; 538 break; 539 default: 540 on = 0; 541 duration = 0; 542 } 543 if (on) { 544 ioctl(line, TIOCMGET, &lflags); 545 lflags |= TIOCM_RTS; 546 ioctl(line, TIOCMSET, &lflags); 547 } 548 duration *= 10000; 549 if (duration) 550 usleep(duration); 551 ioctl(line, TIOCMGET, &lflags); 552 lflags &= ~TIOCM_RTS; 553 ioctl(line, TIOCMSET, &lflags); 554 duration = dot_clock * 10000; 555 usleep(duration); 556 } 557 duration = dot_clock * CHAR_SPACE * 10000; 558 usleep(duration); 559} 560 561void 562sighandler(int signo) 563{ 564 565 ioctl(line, TIOCMSET, &olflags); 566 tcsetattr(line, TCSANOW, &otty); 567 568 signal(signo, SIG_DFL); 569 (void)kill(getpid(), signo); 570} 571