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