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 40 static 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 47 static char sccsid[] = "@(#)morse.c 8.1 (Berkeley) 5/31/93"; 48 #endif 49 static const char rcsid[] = 50 "$FreeBSD: src/games/morse/morse.c,v 1.12.2.2 2002/03/12 17:45:15 phantom Exp $"; 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 70 struct morsetab { 71 char inchar; 72 char *morse; 73 }; 74 75 static 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 145 static const struct morsetab iso8859tab[] = { 146 {'�', ".--.-"}, 147 {'�', ".--.-"}, 148 {'�', ".--.-"}, 149 {'�', ".-.-"}, 150 {'�', "-.-.."}, 151 {'�', "..-.."}, 152 {'�', "..-.."}, 153 {'�', "-..-."}, 154 {'�', "---."}, 155 {'�', "..--"}, 156 157 {'\0', ""} 158 }; 159 160 static 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 201 void show(const char *), play(const char *), morse(char); 202 void ttyout(const char *); 203 void 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 209 static int pflag, sflag, eflag; 210 static int wpm = 20; /* words per minute */ 211 #define FREQUENCY 600 212 static int freq = FREQUENCY; 213 static 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) 218 static float dot_clock; 219 int spkr, line; 220 struct termios otty, ntty; 221 int olflags; 222 223 #ifdef SPEAKER 224 tone_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 232 static const struct morsetab *hightab; 233 234 int 235 main(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 354 void 355 morse(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 389 void 390 show(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 400 void 401 play(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 442 void 443 ttyout(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 484 void 485 sighandler(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