morse.c revision 126044
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 126044 2004-02-20 13:46:39Z fanf $"; 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 <machine/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 iso8859tab[] = { 149 {'�', ".--.-"}, 150 {'�', ".--.-"}, 151 {'�', ".--.-"}, 152 {'�', ".-.-"}, 153 {'�', "-.-.."}, 154 {'�', "..-.."}, 155 {'�', "..-.."}, 156 {'�', "-..-."}, 157 {'�', "---."}, 158 {'�', "..--"}, 159 160 {'\0', ""} 161}; 162 163static const struct morsetab koi8rtab[] = { 164 /* 165 * the cyrillic alphabet; you'll need a KOI8R font in order 166 * to see the actual characters 167 */ 168 {'�', ".-"}, /* a */ 169 {'�', "-..."}, /* be */ 170 {'�', ".--"}, /* ve */ 171 {'�', "--."}, /* ge */ 172 {'�', "-.."}, /* de */ 173 {'�', "."}, /* ye */ 174 {'�', "."}, /* yo, the same as ye */ 175 {'�', "...-"}, /* she */ 176 {'�', "--.."}, /* ze */ 177 {'�', ".."}, /* i */ 178 {'�', ".---"}, /* i kratkoye */ 179 {'�', "-.-"}, /* ka */ 180 {'�', ".-.."}, /* el */ 181 {'�', "--"}, /* em */ 182 {'�', "-."}, /* en */ 183 {'�', "---"}, /* o */ 184 {'�', ".--."}, /* pe */ 185 {'�', ".-."}, /* er */ 186 {'�', "..."}, /* es */ 187 {'�', "-"}, /* te */ 188 {'�', "..-"}, /* u */ 189 {'�', "..-."}, /* ef */ 190 {'�', "...."}, /* kha */ 191 {'�', "-.-."}, /* ce */ 192 {'�', "---."}, /* che */ 193 {'�', "----"}, /* sha */ 194 {'�', "--.-"}, /* shcha */ 195 {'�', "-.--"}, /* yi */ 196 {'�', "-..-"}, /* myakhkij znak */ 197 {'�', "..-.."}, /* ae */ 198 {'�', "..--"}, /* yu */ 199 {'�', ".-.-"}, /* ya */ 200 201 {'\0', ""} 202}; 203 204void show(const char *), play(const char *), morse(char); 205void ttyout(const char *); 206void sighandler(int); 207 208#define GETOPTOPTS "d:ef:lsw:" 209#define USAGE \ 210"usage: morse [-els] [-d device] [-w speed] [-f frequency] [string ...]\n" 211 212static int pflag, lflag, sflag, eflag; 213static int wpm = 20; /* words per minute */ 214#define FREQUENCY 600 215static int freq = FREQUENCY; 216static char *device; /* for tty-controlled generator */ 217 218#define DASH_LEN 3 219#define CHAR_SPACE 3 220#define WORD_SPACE (7 - CHAR_SPACE - 1) 221static float dot_clock; 222int spkr, line; 223struct termios otty, ntty; 224int olflags; 225 226#ifdef SPEAKER 227tone_t sound; 228#undef GETOPTOPTS 229#define GETOPTOPTS "d:ef:lpsw:" 230#undef USAGE 231#define USAGE \ 232"usage: morse [-s] [-p] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n" 233#endif 234 235static const struct morsetab *hightab; 236 237int 238main(int argc, char **argv) 239{ 240 int ch, lflags; 241 char *p, *codeset; 242 243 while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1) 244 switch ((char) ch) { 245 case 'd': 246 device = optarg; 247 break; 248 case 'e': 249 eflag = 1; 250 setvbuf(stdout, 0, _IONBF, 0); 251 break; 252 case 'f': 253 freq = atoi(optarg); 254 break; 255 case 'l': 256 lflag = 1; 257 break; 258#ifdef SPEAKER 259 case 'p': 260 pflag = 1; 261 break; 262#endif 263 case 's': 264 sflag = 1; 265 break; 266 case 'w': 267 wpm = atoi(optarg); 268 break; 269 case '?': 270 default: 271 fputs(USAGE, stderr); 272 exit(1); 273 } 274 if (sflag && lflag) { 275 fputs("morse: only one of -l and -s allowed\n", stderr); 276 exit(1); 277 } 278 if ((pflag || device) && (sflag || lflag)) { 279 fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr); 280 exit(1); 281 } 282 if ((pflag || device) && ((wpm < 1) || (wpm > 60))) { 283 fputs("morse: insane speed\n", stderr); 284 exit(1); 285 } 286 if ((pflag || device) && (freq == 0)) 287 freq = FREQUENCY; 288 289#ifdef SPEAKER 290 if (pflag) { 291 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) { 292 perror(SPEAKER); 293 exit(1); 294 } 295 } else 296#endif 297 if (device) { 298 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) { 299 perror("open tty line"); 300 exit(1); 301 } 302 if (tcgetattr(line, &otty) == -1) { 303 perror("tcgetattr() failed"); 304 exit(1); 305 } 306 ntty = otty; 307 ntty.c_cflag |= CLOCAL; 308 tcsetattr(line, TCSANOW, &ntty); 309 lflags = fcntl(line, F_GETFL); 310 lflags &= ~O_NONBLOCK; 311 fcntl(line, F_SETFL, &lflags); 312 ioctl(line, TIOCMGET, &lflags); 313 lflags &= ~TIOCM_RTS; 314 olflags = lflags; 315 ioctl(line, TIOCMSET, &lflags); 316 (void)signal(SIGHUP, sighandler); 317 (void)signal(SIGINT, sighandler); 318 (void)signal(SIGQUIT, sighandler); 319 (void)signal(SIGTERM, sighandler); 320 } 321 if (pflag || device) { 322 dot_clock = wpm / 2.4; /* dots/sec */ 323 dot_clock = 1 / dot_clock; /* duration of a dot */ 324 dot_clock = dot_clock / 2; /* dot_clock runs at twice */ 325 /* the dot rate */ 326 dot_clock = dot_clock * 100; /* scale for ioctl */ 327 } 328 329 argc -= optind; 330 argv += optind; 331 332 if (setlocale(LC_CTYPE, "") != NULL && 333 *(codeset = nl_langinfo(CODESET)) != '\0') { 334 if (strcmp(codeset, "KOI8-R") == 0) 335 hightab = koi8rtab; 336 else if (strcmp(codeset, "ISO8859-1") == 0 || 337 strcmp(codeset, "ISO8859-15") == 0) 338 hightab = iso8859tab; 339 } 340 341 if (lflag) 342 printf("m"); 343 if (*argv) { 344 do { 345 for (p = *argv; *p; ++p) { 346 if (eflag) 347 putchar(*p); 348 morse(*p); 349 } 350 if (eflag) 351 putchar(' '); 352 morse(' '); 353 } while (*++argv); 354 } else { 355 while ((ch = getchar()) != EOF) { 356 if (eflag) 357 putchar(ch); 358 morse(ch); 359 } 360 } 361 if (device) 362 tcsetattr(line, TCSANOW, &otty); 363 exit(0); 364} 365 366void 367morse(char c) 368{ 369 const struct morsetab *m; 370 371 if (isalpha((unsigned char)c)) 372 c = tolower((unsigned char)c); 373 if ((c == '\r') || (c == '\n')) 374 c = ' '; 375 if (c == ' ') { 376 if (pflag) 377 play(" "); 378 else if (device) 379 ttyout(" "); 380 else if (lflag) 381 printf("\n"); 382 else 383 show(""); 384 return; 385 } 386 for (m = ((unsigned char)c < 0x80? mtab: hightab); 387 m != NULL && m->inchar != '\0'; 388 m++) { 389 if (m->inchar == c) { 390 if (pflag) { 391 play(m->morse); 392 } else if (device) { 393 ttyout(m->morse); 394 } else 395 show(m->morse); 396 } 397 } 398} 399 400void 401show(const char *s) 402{ 403 if (lflag) { 404 printf("%s ", s); 405 } else if (sflag) { 406 printf(" %s\n", s); 407 } else { 408 for (; *s; ++s) 409 printf(" %s", *s == '.' ? "dit" : "dah"); 410 printf("\n"); 411 } 412} 413 414void 415play(const char *s) 416{ 417#ifdef SPEAKER 418 const char *c; 419 420 for (c = s; *c != '\0'; c++) { 421 switch (*c) { 422 case '.': 423 sound.frequency = freq; 424 sound.duration = dot_clock; 425 break; 426 case '-': 427 sound.frequency = freq; 428 sound.duration = dot_clock * DASH_LEN; 429 break; 430 case ' ': 431 sound.frequency = 0; 432 sound.duration = dot_clock * WORD_SPACE; 433 break; 434 default: 435 sound.duration = 0; 436 } 437 if (sound.duration) { 438 if (ioctl(spkr, SPKRTONE, &sound) == -1) { 439 perror("ioctl play"); 440 exit(1); 441 } 442 } 443 sound.frequency = 0; 444 sound.duration = dot_clock; 445 if (ioctl(spkr, SPKRTONE, &sound) == -1) { 446 perror("ioctl rest"); 447 exit(1); 448 } 449 } 450 sound.frequency = 0; 451 sound.duration = dot_clock * CHAR_SPACE; 452 ioctl(spkr, SPKRTONE, &sound); 453#endif 454} 455 456void 457ttyout(const char *s) 458{ 459 const char *c; 460 int duration, on, lflags; 461 462 for (c = s; *c != '\0'; c++) { 463 switch (*c) { 464 case '.': 465 on = 1; 466 duration = dot_clock; 467 break; 468 case '-': 469 on = 1; 470 duration = dot_clock * DASH_LEN; 471 break; 472 case ' ': 473 on = 0; 474 duration = dot_clock * WORD_SPACE; 475 break; 476 default: 477 on = 0; 478 duration = 0; 479 } 480 if (on) { 481 ioctl(line, TIOCMGET, &lflags); 482 lflags |= TIOCM_RTS; 483 ioctl(line, TIOCMSET, &lflags); 484 } 485 duration *= 10000; 486 if (duration) 487 usleep(duration); 488 ioctl(line, TIOCMGET, &lflags); 489 lflags &= ~TIOCM_RTS; 490 ioctl(line, TIOCMSET, &lflags); 491 duration = dot_clock * 10000; 492 usleep(duration); 493 } 494 duration = dot_clock * CHAR_SPACE * 10000; 495 usleep(duration); 496} 497 498void 499sighandler(int signo) 500{ 501 502 ioctl(line, TIOCMSET, &olflags); 503 tcsetattr(line, TCSANOW, &otty); 504 505 signal(signo, SIG_DFL); 506 (void)kill(getpid(), signo); 507} 508