xref: /dragonfly/games/morse/morse.c (revision 70675b40)
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. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1988, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)morse.c	8.1 (Berkeley) 5/31/93
31  * $FreeBSD: src/games/morse/morse.c,v 1.12.2.2 2002/03/12 17:45:15 phantom Exp $
32  * $OpenBSD: morse.c,v 1.22 2016/03/07 12:07:56 mestre Exp $
33  */
34 
35 /*
36  * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
37  * <lyndon@orthanc.ca>
38  */
39 
40 #include <sys/time.h>
41 #include <sys/soundcard.h>
42 
43 #include <ctype.h>
44 #include <err.h>
45 #include <fcntl.h>
46 #include <langinfo.h>
47 #include <locale.h>
48 #include <math.h>
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <termios.h>
54 #include <unistd.h>
55 
56 static const char *digit[] = {
57 	"-----",
58 	".----",
59 	"..---",
60 	"...--",
61 	"....-",
62 	".....",
63 	"-....",
64 	"--...",
65 	"---..",
66 	"----.",
67 };
68 
69 static const char *alph[] = {
70 	".-",
71 	"-...",
72 	"-.-.",
73 	"-..",
74 	".",
75 	"..-.",
76 	"--.",
77 	"....",
78 	"..",
79 	".---",
80 	"-.-",
81 	".-..",
82 	"--",
83 	"-.",
84 	"---",
85 	".--.",
86 	"--.-",
87 	".-.",
88 	"...",
89 	"-",
90 	"..-",
91 	"...-",
92 	".--",
93 	"-..-",
94 	"-.--",
95 	"--..",
96 };
97 
98 struct punc {
99 	char c;
100 	const char *morse;
101 };
102 
103 static const struct punc other[] = {
104 	{ 'e', "..-.." },	/* accented e - only decodes */
105 	{ ',', "--..--" },
106 	{ '.', ".-.-.-" },
107 	{ '?', "..--.." },
108 	{ '/', "-..-." },
109 	{ '-', "-....-" },
110 	{ ':', "---..." },
111 	{ ';', "-.-.-." },
112 	{ '(', "-.--." },	/* KN */
113 	{ ')', "-.--.-" },
114 	{ '"', ".-..-." },
115 	{ '`', ".-..-." },
116 	{ '\'', ".----." },
117 	{ '+', ".-.-." },	/* AR \n\n\n */
118 	{ '=', "-...-" },	/* BT \n\n */
119 	{ '@', ".--.-." },
120 	{ '\n', ".-.-" },	/* AA (will only decode) */
121 	{ '\0', NULL }
122 };
123 
124 struct prosign {
125 	const char *c;
126 	const char *morse;
127 };
128 
129 static const struct prosign ps[] = {
130 	{ "<AS>", ".-..." },	/* wait */
131 	{ "<CL>", "-.-..-.." },
132 	{ "<CT>", "-.-.-" },	/* start */
133 	{ "<EE5>", "......" },	/* error */
134 	{ "<EE5>", "......." },
135 	{ "<EE5>", "........" },
136 	{ "<SK>", "...-.-" },
137 	{ "<SN>", "...-." },	/* understood */
138 	{ "<SOS>", "...---..." },
139 	{ NULL, NULL }
140 };
141 
142 struct morsetab {
143 	char            inchar;
144 	const char      *morse;
145 };
146 
147 static const struct morsetab mtab[] = {
148 
149 	/* letters */
150 
151 	{'a', ".-"},
152 	{'b', "-..."},
153 	{'c', "-.-."},
154 	{'d', "-.."},
155 	{'e', "."},
156 	{'f', "..-."},
157 	{'g', "--."},
158 	{'h', "...."},
159 	{'i', ".."},
160 	{'j', ".---"},
161 	{'k', "-.-"},
162 	{'l', ".-.."},
163 	{'m', "--"},
164 	{'n', "-."},
165 	{'o', "---"},
166 	{'p', ".--."},
167 	{'q', "--.-"},
168 	{'r', ".-."},
169 	{'s', "..."},
170 	{'t', "-"},
171 	{'u', "..-"},
172 	{'v', "...-"},
173 	{'w', ".--"},
174 	{'x', "-..-"},
175 	{'y', "-.--"},
176 	{'z', "--.."},
177 
178 	/* digits */
179 
180 	{'0', "-----"},
181 	{'1', ".----"},
182 	{'2', "..---"},
183 	{'3', "...--"},
184 	{'4', "....-"},
185 	{'5', "....."},
186 	{'6', "-...."},
187 	{'7', "--..."},
188 	{'8', "---.."},
189 	{'9', "----."},
190 
191 	/* punctuation */
192 
193 	{',', "--..--"},
194 	{'.', ".-.-.-"},
195 	{'?', "..--.."},
196 	{'!', "-.-.--"},	/* KW */
197 	{'/', "-..-."},
198 	{'-', "-....-"},
199 	{'_', "..--.."},
200 	{'=', "-...-"},		/* BT */
201 	{':', "---..."},
202 	{';', "-.-.-."},
203 	{'(', "-.--."},		/* KN */
204 	{')', "-.--.-"},
205 	{'$', "...-..-"},
206 	{'+', ".-.-."},		/* AR */
207 	{'\'', ".----."},
208 	{'"', ".-..-."},
209 	{'@', ".--.-."},	/* AC */
210 
211 	{'\0', ""}
212 };
213 
214 
215 static const struct morsetab iso8859tab[] = {
216 	{'�', ".--.-"},
217 	{'�', ".--.-"},
218 	{'�', ".--.-"},
219 	{'�', ".-.-"},
220 	{'�', "-.-.."},
221 	{'�', "..-.."},
222 	{'�', "..-.."},
223 	{'�', "-..-."},
224 	{'�', "---."},
225 	{'�', "..--"},
226 
227 	{'\0', ""}
228 };
229 
230 static const struct morsetab koi8rtab[] = {
231 	/*
232 	 * the cyrillic alphabet; you'll need a KOI8R font in order
233 	 * to see the actual characters
234 	 */
235 	{'�', ".-"},		/* a */
236 	{'�', "-..."},	/* be */
237 	{'�', ".--"},	/* ve */
238 	{'�', "--."},	/* ge */
239 	{'�', "-.."},	/* de */
240 	{'�', "."},		/* ye */
241 	{'�', "."},		/* yo, the same as ye */
242 	{'�', "...-"},	/* she */
243 	{'�', "--.."},	/* ze */
244 	{'�', ".."},		/* i */
245 	{'�', ".---"},	/* i kratkoye */
246 	{'�', "-.-"},	/* ka */
247 	{'�', ".-.."},	/* el */
248 	{'�', "--"},		/* em */
249 	{'�', "-."},		/* en */
250 	{'�', "---"},	/* o */
251 	{'�', ".--."},	/* pe */
252 	{'�', ".-."},	/* er */
253 	{'�', "..."},	/* es */
254 	{'�', "-"},		/* te */
255 	{'�', "..-"},	/* u */
256 	{'�', "..-."},	/* ef */
257 	{'�', "...."},	/* kha */
258 	{'�', "-.-."},	/* ce */
259 	{'�', "---."},	/* che */
260 	{'�', "----"},	/* sha */
261 	{'�', "--.-"},	/* shcha */
262 	{'�', "-.--"},	/* yi */
263 	{'�', "-..-"},	/* myakhkij znak */
264 	{'�', "..-.."},	/* ae */
265 	{'�', "..--"},	/* yu */
266 	{'�', ".-.-"},	/* ya */
267 
268 	{'\0', ""}
269 };
270 
271 struct tone_data {
272 	int16_t	*data;
273 	size_t	len;
274 };
275 
276 static void		alloc_soundbuf(struct tone_data *, double, int);
277 static void		morse(char, int);
278 static void		decode(const char *);
279 static void		show(const char *, int);
280 static void		play(const char *, int);
281 static void		ttyout(const char *, int);
282 static void		sighandler(int);
283 
284 #define GETOPTOPTS "d:ef:lopP:rsw:W:"
285 #define USAGE \
286 "usage: morse [-r] [-els] [-p | -o] [-P device] [-d device] [-w speed] [-W speed] [-f frequency] [string ...]\n"
287 
288 static int      lflag, oflag, pflag, rflag, sflag, eflag;
289 static int      wpm = 20;	/* words per minute */
290 static int	farnsworth = -1;
291 #define FREQUENCY 600
292 static int      freq = FREQUENCY;
293 static char	*device;	/* for tty-controlled generator */
294 
295 static struct tone_data tone_dot, tone_dash, tone_silence, tone_letter_silence;
296 #define DSP_RATE 44100
297 static const char *snddev = NULL;
298 
299 #define DASH_LEN 3
300 #define CHAR_SPACE 3
301 #define WORD_SPACE (7 - CHAR_SPACE)
302 static float    dot_clock, word_clock;
303 int             spkr, line;
304 struct termios	otty, ntty;
305 int		olflags;
306 
307 static const struct morsetab *hightab;
308 
309 int
310 main(int argc, char *argv[])
311 {
312 	int    ch, lflags;
313 	int    prosign;
314 	char  *p, *codeset;
315 
316 	while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
317 		switch ((char) ch) {
318 		case 'd':
319 			device = optarg;
320 			break;
321 		case 'e':
322 			eflag = 1;
323 			setvbuf(stdout, 0, _IONBF, 0);
324 			break;
325 		case 'f':
326 			freq = atoi(optarg);
327 			break;
328 		case 'l':
329 			lflag = 1;
330 			break;
331 		case 'o':
332 			oflag = 1;
333 			/* FALLTHROUGH */
334 		case 'p':
335 			pflag = 1;
336 			break;
337 		case 'P':
338 			snddev = optarg;
339 			break;
340 		case 'r':
341 			rflag = 1;
342 			break;
343 		case 's':
344 			sflag = 1;
345 			break;
346 		case 'w':
347 			wpm = atoi(optarg);
348 			break;
349 		case 'W':
350 			farnsworth = atoi(optarg);
351 			break;
352 		case '?':
353 		default:
354 			fputs(USAGE, stderr);
355 			exit(1);
356 		}
357 	if (sflag && lflag) {
358 		fputs("morse: only one of -l and -s allowed\n", stderr);
359 		exit(1);
360 	}
361 	if (pflag + !!device + sflag + lflag > 1) {
362 		fputs("morse: only one of -o, -p, -d and -l, -s allowed\n", stderr);
363 		exit(1);
364 	}
365 	if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (farnsworth > 60))) {
366 		fputs("morse: insane speed\n", stderr);
367 		exit(1);
368 	}
369 	if ((pflag || device) && (freq == 0))
370 		freq = FREQUENCY;
371 	if (pflag || device) {
372 		/*
373 		 * A note on how to get to this magic 1.2:
374 		 * x WPM = 50*x dits per minute (norm word "PARIS").
375 		 * dits per sec = dits per minute / 60, thus
376 		 * dits per sec = 50 * x / 60 = x / (60 / 50) = x / 1.2
377 		 */
378 		dot_clock = wpm / 1.2;		/* dots/sec */
379 		dot_clock = 1 / dot_clock;	/* duration of a dot */
380 
381 		word_clock = dot_clock;
382 
383 		/*
384 		 * This is how to get to this formula:
385 		 * PARIS = 22 dit (symbols) + 9 symbol spaces = 31 symbol times
386 		 *       + 19 space times.
387 		 *
388 		 * The symbol times are in dot_clock, so the spaces have to
389 		 * make up to reach the farnsworth time.
390 		 */
391 		if (farnsworth > 0)
392 			word_clock = (60.0 / farnsworth - 31 * dot_clock) / 19;
393 	}
394 	if (snddev == NULL) {
395 		if (oflag)
396 			snddev = "-";
397 		else /* only pflag */
398 			snddev = "/dev/dsp";
399 	}
400 
401 	if (pflag) {
402 		snd_chan_param param;
403 
404 		if (oflag && strcmp(snddev, "-") == 0)
405 			spkr = STDOUT_FILENO;
406 		else
407 			spkr = open(snddev, O_WRONLY, 0);
408 		if (spkr == -1)
409 			err(1, "%s", snddev);
410 		param.play_rate = DSP_RATE;
411 		param.play_format = AFMT_S16_NE;
412 		param.rec_rate = 0;
413 		param.rec_format = 0;
414 		if (!oflag && ioctl(spkr, AIOSFMT, &param) != 0)
415 			err(1, "%s: set format", snddev);
416 		alloc_soundbuf(&tone_dot, dot_clock, 1);
417 		alloc_soundbuf(&tone_dash, DASH_LEN * dot_clock, 1);
418 		alloc_soundbuf(&tone_silence, dot_clock, 0);
419 		alloc_soundbuf(&tone_letter_silence, word_clock, 0);
420 	} else
421 	if (device) {
422 		if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
423 			perror("open tty line");
424 			exit(1);
425 		}
426 		if (tcgetattr(line, &otty) == -1) {
427 			perror("tcgetattr() failed");
428 			exit(1);
429 		}
430 		ntty = otty;
431 		ntty.c_cflag |= CLOCAL;
432 		tcsetattr(line, TCSANOW, &ntty);
433 		lflags = fcntl(line, F_GETFL);
434 		lflags &= ~O_NONBLOCK;
435 		fcntl(line, F_SETFL, &lflags);
436 		ioctl(line, TIOCMGET, &lflags);
437 		lflags &= ~TIOCM_RTS;
438 		olflags = lflags;
439 		ioctl(line, TIOCMSET, &lflags);
440 		signal(SIGHUP, sighandler);
441 		signal(SIGINT, sighandler);
442 		signal(SIGQUIT, sighandler);
443 		signal(SIGTERM, sighandler);
444 	}
445 
446 	argc -= optind;
447 	argv += optind;
448 
449 	if (setlocale(LC_CTYPE, "") != NULL &&
450 	    *(codeset = nl_langinfo(CODESET)) != '\0') {
451 		if (strcmp(codeset, "KOI8-R") == 0)
452 			hightab = koi8rtab;
453 		else if (strcmp(codeset, "ISO8859-1") == 0 ||
454 			 strcmp(codeset, "ISO8859-15") == 0)
455 			hightab = iso8859tab;
456 	}
457 
458 	if (rflag) {
459 		if (*argv) {
460 			do {
461 				decode(*argv);
462 			} while (*++argv);
463 		} else {
464 			char foo[10];	/* All morse chars shorter than this */
465 			int blank, i;
466 
467 			i = 0;
468 			blank = 0;
469 			while ((ch = getchar()) != EOF) {
470 				if (ch == '-' || ch == '.') {
471 					foo[i++] = ch;
472 					if (i == 10) {
473 						/* overrun means gibberish--print 'x' and
474 						 * advance */
475 						i = 0;
476 						putchar('x');
477 						while ((ch = getchar()) != EOF &&
478 						    (ch == '.' || ch == '-'))
479 							;
480 						blank = 1;
481 					}
482 				} else if (i) {
483 					foo[i] = '\0';
484 					decode(foo);
485 					i = 0;
486 					blank = 0;
487 				} else if (isspace(ch)) {
488 					if (blank) {
489 						/* print whitespace for each double blank */
490 						putchar(' ');
491 						blank = 0;
492 					} else
493 						blank = 1;
494 				}
495 			}
496 		}
497 		putchar('\n');
498 		exit(0);
499 	}
500 
501 	if (lflag)
502 		printf("m");
503 	if (*argv) {
504 		do {
505 			prosign = 0;
506 			for (p = *argv; *p; ++p) {
507 				if (eflag)
508 					putchar(*p);
509 				if (*p == '<' || *p == '>') {
510 					prosign = *p == '<';
511 					continue;
512 				}
513 				if (strchr("> \r\n", *(p + 1)) != NULL)
514 					prosign = 0;
515 				morse(*p, prosign);
516 			}
517 			if (eflag)
518 				putchar(' ');
519 			morse(' ', 0);
520 		} while (*++argv);
521 	} else {
522 		prosign = 0;
523 		while ((ch = getchar()) != EOF) {
524 			if (eflag)
525 				putchar(ch);
526 			if (ch == '<') {
527 				prosign = 1;
528 				continue;
529 			}
530 			if (prosign) {
531 				int tch;
532 
533 				tch = getchar();
534 				if (strchr("> \r\n", tch) != NULL)
535 					prosign = 0;
536 				if (tch != '>')
537 					ungetc(tch, stdin);
538 			}
539 			morse(ch, prosign);
540 		}
541 	}
542 	if (device)
543 		tcsetattr(line, TCSANOW, &otty);
544 	exit(0);
545 }
546 
547 static void
548 alloc_soundbuf(struct tone_data *tone, double len, int on)
549 {
550 	int samples, i;
551 
552 	samples = DSP_RATE * len;
553 	tone->len = samples * sizeof(*tone->data);
554 	tone->data = malloc(tone->len);
555 	if (tone->data == NULL)
556 		err(1, NULL);
557 	if (!on) {
558 		bzero(tone->data, tone->len);
559 		return;
560 	}
561 
562 	/*
563 	 * We create a sinus with the specified frequency and smooth
564 	 * the edges to reduce key clicks.
565 	 */
566 	for (i = 0; i < samples; i++) {
567 		double filter = 1;
568 
569 #define FILTER_SAMPLES (DSP_RATE * 8 / 1000)	/* 8 ms ramp time */
570 		if (i < FILTER_SAMPLES || i > samples - FILTER_SAMPLES) {
571 			int fi = i;
572 
573 			if (i > FILTER_SAMPLES)
574 				fi = samples - i;
575 #if defined(TRIANGLE_FILTER)
576 			/*
577 			 * Triangle envelope
578 			 */
579 			filter = (double)fi / FILTER_SAMPLES;
580 #elif defined(GAUSS_FILTER)
581 			/*
582 			 * Gauss envelope
583 			 */
584 			filter = exp(-4.0 *
585 				     pow((double)(FILTER_SAMPLES - fi) /
586 					 FILTER_SAMPLES, 2));
587 #else
588 			/*
589 			 * Cosine envelope
590 			 */
591 			filter = (1 + cos(M_PI * (FILTER_SAMPLES - fi) / FILTER_SAMPLES)) / 2;
592 #endif
593 		}
594 		tone->data[i] = 32767 * sin((double)i / samples * len * freq * 2 * M_PI) *
595 		    filter;
596 	}
597 }
598 
599 static void
600 morse(char c, int prosign)
601 {
602 	const struct morsetab *m;
603 
604 	if (isalpha((unsigned char)c))
605 		c = tolower((unsigned char)c);
606 	if ((c == '\r') || (c == '\n'))
607 		c = ' ';
608 	if (c == ' ') {
609 		if (pflag) {
610 			play(" ", 0);
611 			return;
612 		} else if (device) {
613 			ttyout(" ", 0);
614 			return;
615 		} else if (lflag) {
616 			printf("\n");
617 		} else {
618 			show("", 0);
619 			return;
620 		}
621 	}
622 	for (m = ((unsigned char)c < 0x80? mtab: hightab);
623 	     m != NULL && m->inchar != '\0';
624 	     m++) {
625 		if (m->inchar == c) {
626 			if (pflag) {
627 				play(m->morse, prosign);
628 			} else if (device) {
629 				ttyout(m->morse, prosign);
630 			} else
631 				show(m->morse, prosign);
632 		}
633 	}
634 }
635 
636 static void
637 decode(const char *s)
638 {
639 	int i;
640 
641 	for (i = 0; i < 10; i++)
642 		if (strcmp(digit[i], s) == 0) {
643 			putchar('0' + i);
644 			return;
645 		}
646 
647 	for (i = 0; i < 26; i++)
648 		if (strcmp(alph[i], s) == 0) {
649 			putchar('A' + i);
650 			return;
651 		}
652 	i = 0;
653 	while (other[i].c) {
654 		if (strcmp(other[i].morse, s) == 0) {
655 			putchar(other[i].c);
656 			return;
657 		}
658 		i++;
659 	}
660 	i = 0;
661 	while (ps[i].c) {
662 		/* put whitespace around prosigns */
663 		if (strcmp(ps[i].morse, s) == 0) {
664 			printf(" %s ", ps[i].c);
665 			return;
666 		}
667 		i++;
668 	}
669 	putchar('x');	/* line noise */
670 }
671 
672 static void
673 show(const char *s, int prosign)
674 {
675 	if (lflag) {
676 		printf("%s ", s);
677 		return;
678 	} else if (sflag)
679 		printf(" %s", s);
680 	else
681 		for (; *s; ++s)
682 			printf(" %s", *s == '.' ? "dit" : "dah");
683 	if (!prosign)
684 		printf("\n");
685 }
686 
687 static void
688 play(const char *s, int prosign)
689 {
690 	const char *c;
691 	int duration;
692 	struct tone_data *tone;
693 
694 	/*
695 	 * We don't need to usleep() here, as the sound device blocks.
696 	 */
697 	for (c = s; *c != '\0'; c++) {
698 		switch (*c) {
699 		case '.':
700 			duration = 1;
701 			tone = &tone_dot;
702 			break;
703 		case '-':
704 			duration = 1;
705 			tone = &tone_dash;
706 			break;
707 		case ' ':
708 			duration = WORD_SPACE;
709 			tone = &tone_letter_silence;
710 			break;
711 		default:
712 			errx(1, "invalid morse digit");
713 		}
714 		while (duration-- > 0)
715 			write(spkr, tone->data, tone->len);
716 		/* Only space within a symbol */
717 		if (c[1] != '\0' || prosign)
718 			write(spkr, tone_silence.data, tone_silence.len);
719 	}
720 	if (prosign)
721 		return;
722 	duration = CHAR_SPACE;
723 	while (duration-- > 0)
724 		write(spkr, tone_letter_silence.data, tone_letter_silence.len);
725 
726 	/* Sync out the audio data with other output */
727 	if (!oflag)
728 		ioctl(spkr, SNDCTL_DSP_SYNC, NULL);
729 }
730 
731 static void
732 ttyout(const char *s, int prosign)
733 {
734 	const char *c;
735 	int duration, on, lflags;
736 
737 	for (c = s; *c != '\0'; c++) {
738 		switch (*c) {
739 		case '.':
740 			on = 1;
741 			duration = dot_clock;
742 			break;
743 		case '-':
744 			on = 1;
745 			duration = dot_clock * DASH_LEN;
746 			break;
747 		case ' ':
748 			on = 0;
749 			duration = word_clock * WORD_SPACE;
750 			break;
751 		default:
752 			on = 0;
753 			duration = 0;
754 		}
755 		if (on) {
756 			ioctl(line, TIOCMGET, &lflags);
757 			lflags |= TIOCM_RTS;
758 			ioctl(line, TIOCMSET, &lflags);
759 		}
760 		duration *= 1000000;
761 		if (duration)
762 			usleep(duration);
763 		ioctl(line, TIOCMGET, &lflags);
764 		lflags &= ~TIOCM_RTS;
765 		ioctl(line, TIOCMSET, &lflags);
766 		duration = dot_clock * 1000000;
767 		/* Only space within a symbol */
768 		if (c[1] != '\0' || prosign)
769 			usleep(duration);
770 	}
771 	if (!prosign) {
772 		duration = word_clock * CHAR_SPACE * 1000000;
773 		usleep(duration);
774 	}
775 }
776 
777 static void
778 sighandler(int signo)
779 {
780 
781 	ioctl(line, TIOCMSET, &olflags);
782 	tcsetattr(line, TCSANOW, &otty);
783 
784 	signal(signo, SIG_DFL);
785 	kill(getpid(), signo);
786 }
787