morse.c revision 129114
1293531Sdchagin/*
2293531Sdchagin * Copyright (c) 1988, 1993
3293531Sdchagin *	The Regents of the University of California.  All rights reserved.
4293531Sdchagin *
5293531Sdchagin * Redistribution and use in source and binary forms, with or without
6294369Sjhb * modification, are permitted provided that the following conditions
7293531Sdchagin * are met:
8293531Sdchagin * 1. Redistributions of source code must retain the above copyright
9293531Sdchagin *    notice, this list of conditions and the following disclaimer.
10293531Sdchagin * 2. Redistributions in binary form must reproduce the above copyright
11293531Sdchagin *    notice, this list of conditions and the following disclaimer in the
12293531Sdchagin *    documentation and/or other materials provided with the distribution.
13293531Sdchagin * 3. All advertising materials mentioning features or use of this software
14293531Sdchagin *    must display the following acknowledgement:
15293531Sdchagin *	This product includes software developed by the University of
16293531Sdchagin *	California, Berkeley and its contributors.
17293531Sdchagin * 4. Neither the name of the University nor the names of its contributors
18293531Sdchagin *    may be used to endorse or promote products derived from this software
19293531Sdchagin *    without specific prior written permission.
20293531Sdchagin *
21293531Sdchagin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22293531Sdchagin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23293531Sdchagin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24293531Sdchagin * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25293531Sdchagin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26293531Sdchagin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27293531Sdchagin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28293531Sdchagin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29293531Sdchagin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30293531Sdchagin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31293531Sdchagin * SUCH DAMAGE.
32293531Sdchagin */
33293531Sdchagin
34293531Sdchagin/*
35293531Sdchagin * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
36293531Sdchagin * <lyndon@orthanc.com>
37293531Sdchagin */
38293531Sdchagin
39293531Sdchagin#ifndef lint
40293531Sdchaginstatic const char copyright[] =
41293531Sdchagin"@(#) Copyright (c) 1988, 1993\n\
42293531Sdchagin	The Regents of the University of California.  All rights reserved.\n";
43293531Sdchagin#endif /* not lint */
44293531Sdchagin
45293531Sdchagin#ifndef lint
46293531Sdchagin#if 0
47293531Sdchaginstatic char sccsid[] = "@(#)morse.c	8.1 (Berkeley) 5/31/93";
48293531Sdchagin#endif
49293531Sdchaginstatic const char rcsid[] =
50293531Sdchagin "$FreeBSD: head/games/morse/morse.c 129114 2004-05-11 11:11:14Z dds $";
51293531Sdchagin#endif /* not lint */
52293531Sdchagin
53293531Sdchagin#include <sys/time.h>
54293531Sdchagin
55293531Sdchagin#include <ctype.h>
56293531Sdchagin#include <fcntl.h>
57293531Sdchagin#include <langinfo.h>
58293531Sdchagin#include <locale.h>
59293531Sdchagin#include <signal.h>
60293531Sdchagin#include <stdio.h>
61293531Sdchagin#include <stdlib.h>
62293531Sdchagin#include <string.h>
63293531Sdchagin#include <termios.h>
64293531Sdchagin#include <unistd.h>
65293531Sdchagin
66293531Sdchagin#ifdef SPEAKER
67293531Sdchagin#include <machine/speaker.h>
68293531Sdchagin#endif
69293531Sdchagin
70293531Sdchaginstruct morsetab {
71293531Sdchagin	char            inchar;
72293531Sdchagin	char           *morse;
73293531Sdchagin};
74293531Sdchagin
75293531Sdchaginstatic const struct morsetab mtab[] = {
76293531Sdchagin
77293531Sdchagin	/* letters */
78293531Sdchagin
79293531Sdchagin	{'a', ".-"},
80293531Sdchagin	{'b', "-..."},
81293531Sdchagin	{'c', "-.-."},
82293531Sdchagin	{'d', "-.."},
83293531Sdchagin	{'e', "."},
84293531Sdchagin	{'f', "..-."},
85293531Sdchagin	{'g', "--."},
86293531Sdchagin	{'h', "...."},
87293531Sdchagin	{'i', ".."},
88293531Sdchagin	{'j', ".---"},
89293531Sdchagin	{'k', "-.-"},
90293531Sdchagin	{'l', ".-.."},
91293531Sdchagin	{'m', "--"},
92293531Sdchagin	{'n', "-."},
93293531Sdchagin	{'o', "---"},
94293531Sdchagin	{'p', ".--."},
95293531Sdchagin	{'q', "--.-"},
96293531Sdchagin	{'r', ".-."},
97293531Sdchagin	{'s', "..."},
98293531Sdchagin	{'t', "-"},
99293531Sdchagin	{'u', "..-"},
100293531Sdchagin	{'v', "...-"},
101293531Sdchagin	{'w', ".--"},
102293531Sdchagin	{'x', "-..-"},
103293531Sdchagin	{'y', "-.--"},
104293531Sdchagin	{'z', "--.."},
105293531Sdchagin
106293531Sdchagin	/* digits */
107293531Sdchagin
108293531Sdchagin	{'0', "-----"},
109293531Sdchagin	{'1', ".----"},
110293531Sdchagin	{'2', "..---"},
111293531Sdchagin	{'3', "...--"},
112293531Sdchagin	{'4', "....-"},
113293531Sdchagin	{'5', "....."},
114293531Sdchagin	{'6', "-...."},
115293531Sdchagin	{'7', "--..."},
116293531Sdchagin	{'8', "---.."},
117293531Sdchagin	{'9', "----."},
118293531Sdchagin
119293531Sdchagin	/* punctuation */
120293531Sdchagin
121293531Sdchagin	{',', "--..--"},
122293531Sdchagin	{'.', ".-.-.-"},
123293531Sdchagin	{'"', ".-..-."},
124293531Sdchagin	{'!', "..--."},
125293531Sdchagin	{'?', "..--.."},
126293531Sdchagin	{'/', "-..-."},
127293531Sdchagin	{'-', "-....-"},
128293531Sdchagin	{'=', "-...-"},		/* BT */
129293531Sdchagin	{':', "---..."},
130293531Sdchagin	{';', "-.-.-."},
131293531Sdchagin	{'(', "-.--."},		/* KN */
132293531Sdchagin	{')', "-.--.-"},
133293531Sdchagin	{'$', "...-..-"},
134293531Sdchagin	{'+', ".-.-."},		/* AR */
135293531Sdchagin	{'@', ".--.-."},	/* AC */
136293531Sdchagin
137293531Sdchagin	/* prosigns without already assigned values */
138293531Sdchagin
139293531Sdchagin	{'#', ".-..."},		/* AS */
140293531Sdchagin	{'&', "...-.-"},	/* SK */
141293531Sdchagin	{'*', "...-."},		/* VE */
142293531Sdchagin	{'%', "-...-.-"},	/* BK */
143293531Sdchagin
144293531Sdchagin	{'\0', ""}
145293531Sdchagin};
146293531Sdchagin
147293531Sdchagin
148293531Sdchaginstatic const struct morsetab iso8859_1tab[] = {
149293531Sdchagin	{'�', ".--.-"},
150293531Sdchagin	{'�', ".--.-"},
151293531Sdchagin	{'�', ".--.-"},
152293531Sdchagin	{'�', ".-.-"},
153293531Sdchagin	{'�', "-.-.."},
154293531Sdchagin	{'�', "..-.."},
155293531Sdchagin	{'�', "..-.."},
156293531Sdchagin	{'�', "-..-."},
157293531Sdchagin	{'�', "---."},
158293531Sdchagin	{'�', "..--"},
159293531Sdchagin
160293531Sdchagin	{'\0', ""}
161293531Sdchagin};
162293531Sdchagin
163293531Sdchaginstatic const struct morsetab iso8859_7tab[] = {
164293531Sdchagin	/*
165293531Sdchagin	 * The greek alphabet; you'll need an 8859-7 font in order
166293531Sdchagin	 * to see the actual characters.
167293531Sdchagin	 * This table does not implement:
168293531Sdchagin	 * - the special sequences for the seven diphthongs,
169293531Sdchagin	 * - the punctuation differences.
170293531Sdchagin	 * Implementing these features would introduce too many
171293531Sdchagin	 * special-cases in the program's main loop.
172293531Sdchagin	 * The diphtong sequences are:
173293531Sdchagin	 * alpha iota		.-.-
174293531Sdchagin	 * alpha upsilon	..--
175293531Sdchagin	 * epsilon upsilon	---.
176293531Sdchagin	 * eta upsilon		...-
177293531Sdchagin	 * omikron iota		---..
178293531Sdchagin	 * omikron upsilon	..-
179293531Sdchagin	 * upsilon iota		.---
180293531Sdchagin	 * The different punctuation symbols are:
181293531Sdchagin	 * ;	..-.-
182293531Sdchagin	 * !	--..--
183293531Sdchagin	 */
184293531Sdchagin	{'�', ".-"},	/* alpha */
185293531Sdchagin	{'�', ".-"},	/* alpha with acute */
186293531Sdchagin	{'�', "-..."},	/* beta */
187293531Sdchagin	{'�', "--."},	/* gamma */
188293531Sdchagin	{'�', "-.."},	/* delta */
189293531Sdchagin	{'�', "."},	/* epsilon */
190293531Sdchagin	{'�', "."},	/* epsilon with acute */
191293531Sdchagin	{'�', "--.."},	/* zeta */
192293531Sdchagin	{'�', "...."},	/* eta */
193293531Sdchagin	{'�', "...."},	/* eta with acute */
194293531Sdchagin	{'�', "-.-."},	/* theta */
195293531Sdchagin	{'�', ".."},	/* iota */
196293531Sdchagin	{'�', ".."},	/* iota with acute */
197293531Sdchagin	{'�', ".."},	/* iota with diairesis */
198293531Sdchagin	{'�', ".."},	/* iota with acute and diairesis */
199293531Sdchagin	{'�', "-.-"},	/* kappa */
200293531Sdchagin	{'�', ".-.."},	/* lamda */
201293531Sdchagin	{'�', "--"},	/* mu */
202293531Sdchagin	{'�', "-."},	/* nu */
203293531Sdchagin	{'�', "-..-"},	/* xi */
204293531Sdchagin	{'�', "---"},	/* omicron */
205293531Sdchagin	{'�', "---"},	/* omicron with acute */
206293531Sdchagin	{'�', ".--."},	/* pi */
207293531Sdchagin	{'�', ".-."},	/* rho */
208293531Sdchagin	{'�', "..."},	/* sigma */
209293531Sdchagin	{'�', "..."},	/* final sigma */
210293531Sdchagin	{'�', "-"},	/* tau */
211293531Sdchagin	{'�', "-.--"},	/* upsilon */
212293531Sdchagin	{'�', "-.--"},	/* upsilon with acute */
213293531Sdchagin	{'�', "-.--"},	/* upsilon and diairesis */
214293531Sdchagin	{'�', "-.--"},	/* upsilon with acute and diairesis */
215293531Sdchagin	{'�', "..-."},	/* phi */
216293531Sdchagin	{'�', "----"},	/* chi */
217293531Sdchagin	{'�', "--.-"},	/* psi */
218293531Sdchagin	{'�', ".--"},	/* omega */
219293531Sdchagin	{'�', ".--"},	/* omega with acute */
220293531Sdchagin
221293531Sdchagin	{'\0', ""}
222293531Sdchagin};
223293531Sdchagin
224293531Sdchaginstatic const struct morsetab koi8rtab[] = {
225293531Sdchagin	/*
226293531Sdchagin	 * the cyrillic alphabet; you'll need a KOI8R font in order
227293531Sdchagin	 * to see the actual characters
228293531Sdchagin	 */
229293531Sdchagin	{'�', ".-"},		/* a */
230293531Sdchagin	{'�', "-..."},	/* be */
231293531Sdchagin	{'�', ".--"},	/* ve */
232293531Sdchagin	{'�', "--."},	/* ge */
233293531Sdchagin	{'�', "-.."},	/* de */
234293531Sdchagin	{'�', "."},		/* ye */
235293531Sdchagin	{'�', "."},         	/* yo, the same as ye */
236293531Sdchagin	{'�', "...-"},	/* she */
237293531Sdchagin	{'�', "--.."},	/* ze */
238293531Sdchagin	{'�', ".."},		/* i */
239293531Sdchagin	{'�', ".---"},	/* i kratkoye */
240293531Sdchagin	{'�', "-.-"},	/* ka */
241293531Sdchagin	{'�', ".-.."},	/* el */
242293531Sdchagin	{'�', "--"},		/* em */
243293531Sdchagin	{'�', "-."},		/* en */
244293531Sdchagin	{'�', "---"},	/* o */
245293531Sdchagin	{'�', ".--."},	/* pe */
246293531Sdchagin	{'�', ".-."},	/* er */
247293531Sdchagin	{'�', "..."},	/* es */
248293531Sdchagin	{'�', "-"},		/* te */
249293531Sdchagin	{'�', "..-"},	/* u */
250293531Sdchagin	{'�', "..-."},	/* ef */
251293531Sdchagin	{'�', "...."},	/* kha */
252293531Sdchagin	{'�', "-.-."},	/* ce */
253293531Sdchagin	{'�', "---."},	/* che */
254293531Sdchagin	{'�', "----"},	/* sha */
255293531Sdchagin	{'�', "--.-"},	/* shcha */
256293531Sdchagin	{'�', "-.--"},	/* yi */
257293531Sdchagin	{'�', "-..-"},	/* myakhkij znak */
258293531Sdchagin	{'�', "..-.."},	/* ae */
259293531Sdchagin	{'�', "..--"},	/* yu */
260293531Sdchagin	{'�', ".-.-"},	/* ya */
261293531Sdchagin
262293531Sdchagin	{'\0', ""}
263293531Sdchagin};
264293531Sdchagin
265293531Sdchaginvoid            show(const char *), play(const char *), morse(char);
266293531Sdchaginvoid		ttyout(const char *);
267293531Sdchaginvoid		sighandler(int);
268293531Sdchagin
269293531Sdchagin#define GETOPTOPTS "d:ef:lsw:"
270293531Sdchagin#define USAGE \
271293531Sdchagin"usage: morse [-els] [-d device] [-w speed] [-f frequency] [string ...]\n"
272293531Sdchagin
273293531Sdchaginstatic int      pflag, lflag, sflag, eflag;
274293531Sdchaginstatic int      wpm = 20;	/* words per minute */
275293531Sdchagin#define FREQUENCY 600
276293531Sdchaginstatic int      freq = FREQUENCY;
277293531Sdchaginstatic char	*device;	/* for tty-controlled generator */
278293531Sdchagin
279293531Sdchagin#define DASH_LEN 3
280293531Sdchagin#define CHAR_SPACE 3
281293531Sdchagin#define WORD_SPACE (7 - CHAR_SPACE - 1)
282293531Sdchaginstatic float    dot_clock;
283293531Sdchaginint             spkr, line;
284293531Sdchaginstruct termios	otty, ntty;
285293531Sdchaginint		olflags;
286293531Sdchagin
287293531Sdchagin#ifdef SPEAKER
288293531Sdchagintone_t          sound;
289293531Sdchagin#undef GETOPTOPTS
290293531Sdchagin#define GETOPTOPTS "d:ef:lpsw:"
291293531Sdchagin#undef USAGE
292293531Sdchagin#define USAGE \
293293531Sdchagin"usage: morse [-s] [-p] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
294293531Sdchagin#endif
295293531Sdchagin
296293531Sdchaginstatic const struct morsetab *hightab;
297293531Sdchagin
298293531Sdchaginint
299293531Sdchaginmain(int argc, char **argv)
300293531Sdchagin{
301293531Sdchagin	int    ch, lflags;
302293531Sdchagin	char  *p, *codeset;
303293531Sdchagin
304293531Sdchagin	while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
305293531Sdchagin		switch ((char) ch) {
306293531Sdchagin		case 'd':
307293531Sdchagin			device = optarg;
308293531Sdchagin			break;
309293531Sdchagin		case 'e':
310293570Sdchagin			eflag = 1;
311			setvbuf(stdout, 0, _IONBF, 0);
312			break;
313		case 'f':
314			freq = atoi(optarg);
315			break;
316		case 'l':
317			lflag = 1;
318			break;
319#ifdef SPEAKER
320		case 'p':
321			pflag = 1;
322			break;
323#endif
324		case 's':
325			sflag = 1;
326			break;
327		case 'w':
328			wpm = atoi(optarg);
329			break;
330		case '?':
331		default:
332			fputs(USAGE, stderr);
333			exit(1);
334		}
335	if (sflag && lflag) {
336		fputs("morse: only one of -l and -s allowed\n", stderr);
337		exit(1);
338	}
339	if ((pflag || device) && (sflag || lflag)) {
340		fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr);
341		exit(1);
342	}
343	if ((pflag || device) && ((wpm < 1) || (wpm > 60))) {
344		fputs("morse: insane speed\n", stderr);
345		exit(1);
346	}
347	if ((pflag || device) && (freq == 0))
348		freq = FREQUENCY;
349
350#ifdef SPEAKER
351	if (pflag) {
352		if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
353			perror(SPEAKER);
354			exit(1);
355		}
356	} else
357#endif
358	if (device) {
359		if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
360			perror("open tty line");
361			exit(1);
362		}
363		if (tcgetattr(line, &otty) == -1) {
364			perror("tcgetattr() failed");
365			exit(1);
366		}
367		ntty = otty;
368		ntty.c_cflag |= CLOCAL;
369		tcsetattr(line, TCSANOW, &ntty);
370		lflags = fcntl(line, F_GETFL);
371		lflags &= ~O_NONBLOCK;
372		fcntl(line, F_SETFL, &lflags);
373		ioctl(line, TIOCMGET, &lflags);
374		lflags &= ~TIOCM_RTS;
375		olflags = lflags;
376		ioctl(line, TIOCMSET, &lflags);
377		(void)signal(SIGHUP, sighandler);
378		(void)signal(SIGINT, sighandler);
379		(void)signal(SIGQUIT, sighandler);
380		(void)signal(SIGTERM, sighandler);
381	}
382	if (pflag || device) {
383		dot_clock = wpm / 2.4;		/* dots/sec */
384		dot_clock = 1 / dot_clock;	/* duration of a dot */
385		dot_clock = dot_clock / 2;	/* dot_clock runs at twice */
386						/* the dot rate */
387		dot_clock = dot_clock * 100;	/* scale for ioctl */
388	}
389
390	argc -= optind;
391	argv += optind;
392
393	if (setlocale(LC_CTYPE, "") != NULL &&
394	    *(codeset = nl_langinfo(CODESET)) != '\0') {
395		if (strcmp(codeset, "KOI8-R") == 0)
396			hightab = koi8rtab;
397		else if (strcmp(codeset, "ISO8859-1") == 0 ||
398			 strcmp(codeset, "ISO8859-15") == 0)
399			hightab = iso8859_1tab;
400		else if (strcmp(codeset, "ISO8859-7") == 0)
401			hightab = iso8859_7tab;
402	}
403
404	if (lflag)
405		printf("m");
406	if (*argv) {
407		do {
408			for (p = *argv; *p; ++p) {
409				if (eflag)
410					putchar(*p);
411				morse(*p);
412			}
413			if (eflag)
414				putchar(' ');
415			morse(' ');
416		} while (*++argv);
417	} else {
418		while ((ch = getchar()) != EOF) {
419			if (eflag)
420				putchar(ch);
421			morse(ch);
422		}
423	}
424	if (device)
425		tcsetattr(line, TCSANOW, &otty);
426	exit(0);
427}
428
429void
430morse(char c)
431{
432	const struct morsetab *m;
433
434	if (isalpha((unsigned char)c))
435		c = tolower((unsigned char)c);
436	if ((c == '\r') || (c == '\n'))
437		c = ' ';
438	if (c == ' ') {
439		if (pflag)
440			play(" ");
441		else if (device)
442			ttyout(" ");
443		else if (lflag)
444			printf("\n");
445		else
446			show("");
447		return;
448	}
449	for (m = ((unsigned char)c < 0x80? mtab: hightab);
450	     m != NULL && m->inchar != '\0';
451	     m++) {
452		if (m->inchar == c) {
453			if (pflag) {
454				play(m->morse);
455			} else if (device) {
456				ttyout(m->morse);
457			} else
458				show(m->morse);
459		}
460	}
461}
462
463void
464show(const char *s)
465{
466	if (lflag) {
467		printf("%s ", s);
468	} else if (sflag) {
469		printf(" %s\n", s);
470	} else {
471		for (; *s; ++s)
472			printf(" %s", *s == '.' ? "dit" : "dah");
473		printf("\n");
474	}
475}
476
477void
478play(const char *s)
479{
480#ifdef SPEAKER
481	const char *c;
482
483	for (c = s; *c != '\0'; c++) {
484		switch (*c) {
485		case '.':
486			sound.frequency = freq;
487			sound.duration = dot_clock;
488			break;
489		case '-':
490			sound.frequency = freq;
491			sound.duration = dot_clock * DASH_LEN;
492			break;
493		case ' ':
494			sound.frequency = 0;
495			sound.duration = dot_clock * WORD_SPACE;
496			break;
497		default:
498			sound.duration = 0;
499		}
500		if (sound.duration) {
501			if (ioctl(spkr, SPKRTONE, &sound) == -1) {
502				perror("ioctl play");
503				exit(1);
504			}
505		}
506		sound.frequency = 0;
507		sound.duration = dot_clock;
508		if (ioctl(spkr, SPKRTONE, &sound) == -1) {
509			perror("ioctl rest");
510			exit(1);
511		}
512	}
513	sound.frequency = 0;
514	sound.duration = dot_clock * CHAR_SPACE;
515	ioctl(spkr, SPKRTONE, &sound);
516#endif
517}
518
519void
520ttyout(const char *s)
521{
522	const char *c;
523	int duration, on, lflags;
524
525	for (c = s; *c != '\0'; c++) {
526		switch (*c) {
527		case '.':
528			on = 1;
529			duration = dot_clock;
530			break;
531		case '-':
532			on = 1;
533			duration = dot_clock * DASH_LEN;
534			break;
535		case ' ':
536			on = 0;
537			duration = dot_clock * WORD_SPACE;
538			break;
539		default:
540			on = 0;
541			duration = 0;
542		}
543		if (on) {
544			ioctl(line, TIOCMGET, &lflags);
545			lflags |= TIOCM_RTS;
546			ioctl(line, TIOCMSET, &lflags);
547		}
548		duration *= 10000;
549		if (duration)
550			usleep(duration);
551		ioctl(line, TIOCMGET, &lflags);
552		lflags &= ~TIOCM_RTS;
553		ioctl(line, TIOCMSET, &lflags);
554		duration = dot_clock * 10000;
555		usleep(duration);
556	}
557	duration = dot_clock * CHAR_SPACE * 10000;
558	usleep(duration);
559}
560
561void
562sighandler(int signo)
563{
564
565	ioctl(line, TIOCMSET, &olflags);
566	tcsetattr(line, TCSANOW, &otty);
567
568	signal(signo, SIG_DFL);
569	(void)kill(getpid(), signo);
570}
571