morse.c revision 126040
148905Srnordier/* 248905Srnordier * Copyright (c) 1988, 1993 348905Srnordier * The Regents of the University of California. All rights reserved. 448905Srnordier * 548905Srnordier * Redistribution and use in source and binary forms, with or without 648905Srnordier * modification, are permitted provided that the following conditions 748905Srnordier * are met: 848905Srnordier * 1. Redistributions of source code must retain the above copyright 948905Srnordier * notice, this list of conditions and the following disclaimer. 1048905Srnordier * 2. Redistributions in binary form must reproduce the above copyright 1148905Srnordier * notice, this list of conditions and the following disclaimer in the 1248905Srnordier * documentation and/or other materials provided with the distribution. 1348905Srnordier * 3. All advertising materials mentioning features or use of this software 1448905Srnordier * must display the following acknowledgement: 1548905Srnordier * This product includes software developed by the University of 1648905Srnordier * California, Berkeley and its contributors. 1748905Srnordier * 4. Neither the name of the University nor the names of its contributors 1848905Srnordier * may be used to endorse or promote products derived from this software 1948905Srnordier * without specific prior written permission. 2048905Srnordier * 2148905Srnordier * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2248905Srnordier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2348905Srnordier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2448905Srnordier * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2548905Srnordier * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2650479Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2748905Srnordier * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2848905Srnordier * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2948905Srnordier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3048905Srnordier * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3148905Srnordier * SUCH DAMAGE. 3248905Srnordier */ 3348905Srnordier 3448905Srnordier/* 3548905Srnordier * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM) 3648905Srnordier * <lyndon@orthanc.com> 3748905Srnordier */ 3848905Srnordier 3948905Srnordier#ifndef lint 4048905Srnordierstatic const char copyright[] = 4148905Srnordier"@(#) Copyright (c) 1988, 1993\n\ 4248905Srnordier The Regents of the University of California. All rights reserved.\n"; 4348905Srnordier#endif /* not lint */ 4448905Srnordier 4548905Srnordier#ifndef lint 4648905Srnordier#if 0 4748905Srnordierstatic char sccsid[] = "@(#)morse.c 8.1 (Berkeley) 5/31/93"; 4848905Srnordier#endif 4948905Srnordierstatic const char rcsid[] = 5048905Srnordier "$FreeBSD: head/games/morse/morse.c 126040 2004-02-20 11:55:38Z fanf $"; 5148905Srnordier#endif /* not lint */ 5248905Srnordier 5348905Srnordier#include <sys/time.h> 5448905Srnordier 5548905Srnordier#include <ctype.h> 5648905Srnordier#include <fcntl.h> 5748905Srnordier#include <langinfo.h> 5848905Srnordier#include <locale.h> 5948905Srnordier#include <signal.h> 6048905Srnordier#include <stdio.h> 6148905Srnordier#include <stdlib.h> 6248905Srnordier#include <string.h> 6348905Srnordier#include <termios.h> 6448905Srnordier#include <unistd.h> 6548905Srnordier 6648905Srnordier#ifdef SPEAKER 6748905Srnordier#include <machine/speaker.h> 6848905Srnordier#endif 6948905Srnordier 7048905Srnordierstruct morsetab { 7148905Srnordier char inchar; 7248905Srnordier char *morse; 7348905Srnordier}; 7448905Srnordier 7548905Srnordierstatic const struct morsetab mtab[] = { 7648905Srnordier 7748905Srnordier /* letters */ 7848905Srnordier 7948905Srnordier {'a', ".-"}, 8048905Srnordier {'b', "-..."}, 8148905Srnordier {'c', "-.-."}, 8248905Srnordier {'d', "-.."}, 8348905Srnordier {'e', "."}, 8448905Srnordier {'f', "..-."}, 8548905Srnordier {'g', "--."}, 8648905Srnordier {'h', "...."}, 8748905Srnordier {'i', ".."}, 8848905Srnordier {'j', ".---"}, 8948905Srnordier {'k', "-.-"}, 9048905Srnordier {'l', ".-.."}, 9148905Srnordier {'m', "--"}, 9248905Srnordier {'n', "-."}, 9348905Srnordier {'o', "---"}, 9448905Srnordier {'p', ".--."}, 9548905Srnordier {'q', "--.-"}, 9648905Srnordier {'r', ".-."}, 9748905Srnordier {'s', "..."}, 9848905Srnordier {'t', "-"}, 9948905Srnordier {'u', "..-"}, 10048905Srnordier {'v', "...-"}, 10148905Srnordier {'w', ".--"}, 10248905Srnordier {'x', "-..-"}, 10348905Srnordier {'y', "-.--"}, 10448905Srnordier {'z', "--.."}, 10548905Srnordier 10648905Srnordier /* digits */ 10748905Srnordier 10848905Srnordier {'0', "-----"}, 10948905Srnordier {'1', ".----"}, 11048905Srnordier {'2', "..---"}, 11148905Srnordier {'3', "...--"}, 11248905Srnordier {'4', "....-"}, 11348905Srnordier {'5', "....."}, 11448905Srnordier {'6', "-...."}, 11548905Srnordier {'7', "--..."}, 11648905Srnordier {'8', "---.."}, 11748905Srnordier {'9', "----."}, 11848905Srnordier 11948905Srnordier /* punctuation */ 12048905Srnordier 12148905Srnordier {',', "--..--"}, 122 {'.', ".-.-.-"}, 123 {'"', ".-..-."}, 124 {'!', "..--."}, 125 {'?', "..--.."}, 126 {'/', "-..-."}, 127 {'-', "-....-"}, 128 {'=', "-...-"}, /* BT */ 129 {':', "---..."}, 130 {';', "-.-.-."}, 131 {'(', "-.--."}, /* KN */ 132 {')', "-.--.-"}, 133 {'$', "...-..-"}, 134 {'+', ".-.-."}, /* AR */ 135 {'@', ".--.-."} 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