morse.c revision 152306
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 152306 2005-11-11 09:57:32Z ru $";
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 <dev/speaker/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	{'/', "-..-."},
127	{'-', "-....-"},
128	{'=', "-...-"},		/* BT */
129	{':', "---..."},
130	{';', "-.-.-."},
131	{'(', "-.--."},		/* KN */
132	{')', "-.--.-"},
133	{'$', "...-..-"},
134	{'+', ".-.-."},		/* AR */
135	{'@', ".--.-."},	/* AC */
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 iso8859_1tab[] = {
149	{'�', ".--.-"},
150	{'�', ".--.-"},
151	{'�', ".--.-"},
152	{'�', ".-.-"},
153	{'�', "-.-.."},
154	{'�', "..-.."},
155	{'�', "..-.."},
156	{'�', "-..-."},
157	{'�', "---."},
158	{'�', "..--"},
159
160	{'\0', ""}
161};
162
163static const struct morsetab iso8859_7tab[] = {
164	/*
165	 * The greek alphabet; you'll need an 8859-7 font in order
166	 * to see the actual characters.
167	 * This table does not implement:
168	 * - the special sequences for the seven diphthongs,
169	 * - the punctuation differences.
170	 * Implementing these features would introduce too many
171	 * special-cases in the program's main loop.
172	 * The diphtong sequences are:
173	 * alpha iota		.-.-
174	 * alpha upsilon	..--
175	 * epsilon upsilon	---.
176	 * eta upsilon		...-
177	 * omikron iota		---..
178	 * omikron upsilon	..-
179	 * upsilon iota		.---
180	 * The different punctuation symbols are:
181	 * ;	..-.-
182	 * !	--..--
183	 */
184	{'�', ".-"},	/* alpha */
185	{'�', ".-"},	/* alpha with acute */
186	{'�', "-..."},	/* beta */
187	{'�', "--."},	/* gamma */
188	{'�', "-.."},	/* delta */
189	{'�', "."},	/* epsilon */
190	{'�', "."},	/* epsilon with acute */
191	{'�', "--.."},	/* zeta */
192	{'�', "...."},	/* eta */
193	{'�', "...."},	/* eta with acute */
194	{'�', "-.-."},	/* theta */
195	{'�', ".."},	/* iota */
196	{'�', ".."},	/* iota with acute */
197	{'�', ".."},	/* iota with diairesis */
198	{'�', ".."},	/* iota with acute and diairesis */
199	{'�', "-.-"},	/* kappa */
200	{'�', ".-.."},	/* lamda */
201	{'�', "--"},	/* mu */
202	{'�', "-."},	/* nu */
203	{'�', "-..-"},	/* xi */
204	{'�', "---"},	/* omicron */
205	{'�', "---"},	/* omicron with acute */
206	{'�', ".--."},	/* pi */
207	{'�', ".-."},	/* rho */
208	{'�', "..."},	/* sigma */
209	{'�', "..."},	/* final sigma */
210	{'�', "-"},	/* tau */
211	{'�', "-.--"},	/* upsilon */
212	{'�', "-.--"},	/* upsilon with acute */
213	{'�', "-.--"},	/* upsilon and diairesis */
214	{'�', "-.--"},	/* upsilon with acute and diairesis */
215	{'�', "..-."},	/* phi */
216	{'�', "----"},	/* chi */
217	{'�', "--.-"},	/* psi */
218	{'�', ".--"},	/* omega */
219	{'�', ".--"},	/* omega with acute */
220
221	{'\0', ""}
222};
223
224static const struct morsetab koi8rtab[] = {
225	/*
226	 * the cyrillic alphabet; you'll need a KOI8R font in order
227	 * to see the actual characters
228	 */
229	{'�', ".-"},		/* a */
230	{'�', "-..."},	/* be */
231	{'�', ".--"},	/* ve */
232	{'�', "--."},	/* ge */
233	{'�', "-.."},	/* de */
234	{'�', "."},		/* ye */
235	{'�', "."},         	/* yo, the same as ye */
236	{'�', "...-"},	/* she */
237	{'�', "--.."},	/* ze */
238	{'�', ".."},		/* i */
239	{'�', ".---"},	/* i kratkoye */
240	{'�', "-.-"},	/* ka */
241	{'�', ".-.."},	/* el */
242	{'�', "--"},		/* em */
243	{'�', "-."},		/* en */
244	{'�', "---"},	/* o */
245	{'�', ".--."},	/* pe */
246	{'�', ".-."},	/* er */
247	{'�', "..."},	/* es */
248	{'�', "-"},		/* te */
249	{'�', "..-"},	/* u */
250	{'�', "..-."},	/* ef */
251	{'�', "...."},	/* kha */
252	{'�', "-.-."},	/* ce */
253	{'�', "---."},	/* che */
254	{'�', "----"},	/* sha */
255	{'�', "--.-"},	/* shcha */
256	{'�', "-.--"},	/* yi */
257	{'�', "-..-"},	/* myakhkij znak */
258	{'�', "..-.."},	/* ae */
259	{'�', "..--"},	/* yu */
260	{'�', ".-.-"},	/* ya */
261
262	{'\0', ""}
263};
264
265void            show(const char *), play(const char *), morse(char);
266void		ttyout(const char *);
267void		sighandler(int);
268
269#define GETOPTOPTS "c:d:ef:lsw:"
270#define USAGE \
271"usage: morse [-els] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
272
273static int      pflag, lflag, sflag, eflag;
274static int      wpm = 20;	/* effective words per minute */
275static int      cpm;		/* effective words per minute between
276				 * characters */
277#define FREQUENCY 600
278static int      freq = FREQUENCY;
279static char	*device;	/* for tty-controlled generator */
280
281#define DASH_LEN 3
282#define CHAR_SPACE 3
283#define WORD_SPACE (7 - CHAR_SPACE - 1)
284static float    dot_clock;
285static float    cdot_clock;
286int             spkr, line;
287struct termios	otty, ntty;
288int		olflags;
289
290#ifdef SPEAKER
291tone_t          sound;
292#undef GETOPTOPTS
293#define GETOPTOPTS "c:d:ef:lpsw:"
294#undef USAGE
295#define USAGE \
296"usage: morse [-elps] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
297#endif
298
299static const struct morsetab *hightab;
300
301int
302main(int argc, char **argv)
303{
304	int    ch, lflags;
305	char  *p, *codeset;
306
307	while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
308		switch ((char) ch) {
309 		case 'c':
310 			cpm = atoi(optarg);
311 			break;
312		case 'd':
313			device = optarg;
314			break;
315		case 'e':
316			eflag = 1;
317			setvbuf(stdout, 0, _IONBF, 0);
318			break;
319		case 'f':
320			freq = atoi(optarg);
321			break;
322		case 'l':
323			lflag = 1;
324			break;
325#ifdef SPEAKER
326		case 'p':
327			pflag = 1;
328			break;
329#endif
330		case 's':
331			sflag = 1;
332			break;
333		case 'w':
334			wpm = atoi(optarg);
335			break;
336		case '?':
337		default:
338			fputs(USAGE, stderr);
339			exit(1);
340		}
341	if (sflag && lflag) {
342		fputs("morse: only one of -l and -s allowed\n", stderr);
343		exit(1);
344	}
345	if ((pflag || device) && (sflag || lflag)) {
346		fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr);
347		exit(1);
348	}
349	if (cpm == 0)
350		cpm = wpm;
351	if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) {
352		fputs("morse: insane speed\n", stderr);
353		exit(1);
354	}
355	if ((pflag || device) && (freq == 0))
356		freq = FREQUENCY;
357
358#ifdef SPEAKER
359	if (pflag) {
360		if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
361			perror(SPEAKER);
362			exit(1);
363		}
364	} else
365#endif
366	if (device) {
367		if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
368			perror("open tty line");
369			exit(1);
370		}
371		if (tcgetattr(line, &otty) == -1) {
372			perror("tcgetattr() failed");
373			exit(1);
374		}
375		ntty = otty;
376		ntty.c_cflag |= CLOCAL;
377		tcsetattr(line, TCSANOW, &ntty);
378		lflags = fcntl(line, F_GETFL);
379		lflags &= ~O_NONBLOCK;
380		fcntl(line, F_SETFL, &lflags);
381		ioctl(line, TIOCMGET, &lflags);
382		lflags &= ~TIOCM_RTS;
383		olflags = lflags;
384		ioctl(line, TIOCMSET, &lflags);
385		(void)signal(SIGHUP, sighandler);
386		(void)signal(SIGINT, sighandler);
387		(void)signal(SIGQUIT, sighandler);
388		(void)signal(SIGTERM, sighandler);
389	}
390	if (pflag || device) {
391		dot_clock = wpm / 2.4;		/* dots/sec */
392		dot_clock = 1 / dot_clock;	/* duration of a dot */
393		dot_clock = dot_clock / 2;	/* dot_clock runs at twice */
394						/* the dot rate */
395		dot_clock = dot_clock * 100;	/* scale for ioctl */
396
397		cdot_clock = cpm / 2.4;		/* dots/sec */
398		cdot_clock = 1 / cdot_clock;	/* duration of a dot */
399		cdot_clock = cdot_clock / 2;	/* dot_clock runs at twice */
400						/* the dot rate */
401		cdot_clock = cdot_clock * 100;	/* scale for ioctl */
402	}
403
404	argc -= optind;
405	argv += optind;
406
407	if (setlocale(LC_CTYPE, "") != NULL &&
408	    *(codeset = nl_langinfo(CODESET)) != '\0') {
409		if (strcmp(codeset, "KOI8-R") == 0)
410			hightab = koi8rtab;
411		else if (strcmp(codeset, "ISO8859-1") == 0 ||
412			 strcmp(codeset, "ISO8859-15") == 0)
413			hightab = iso8859_1tab;
414		else if (strcmp(codeset, "ISO8859-7") == 0)
415			hightab = iso8859_7tab;
416	}
417
418	if (lflag)
419		printf("m");
420	if (*argv) {
421		do {
422			for (p = *argv; *p; ++p) {
423				if (eflag)
424					putchar(*p);
425				morse(*p);
426			}
427			if (eflag)
428				putchar(' ');
429			morse(' ');
430		} while (*++argv);
431	} else {
432		while ((ch = getchar()) != EOF) {
433			if (eflag)
434				putchar(ch);
435			morse(ch);
436		}
437	}
438	if (device)
439		tcsetattr(line, TCSANOW, &otty);
440	exit(0);
441}
442
443void
444morse(char c)
445{
446	const struct morsetab *m;
447
448	if (isalpha((unsigned char)c))
449		c = tolower((unsigned char)c);
450	if ((c == '\r') || (c == '\n'))
451		c = ' ';
452	if (c == ' ') {
453		if (pflag)
454			play(" ");
455		else if (device)
456			ttyout(" ");
457		else if (lflag)
458			printf("\n");
459		else
460			show("");
461		return;
462	}
463	for (m = ((unsigned char)c < 0x80? mtab: hightab);
464	     m != NULL && m->inchar != '\0';
465	     m++) {
466		if (m->inchar == c) {
467			if (pflag) {
468				play(m->morse);
469			} else if (device) {
470				ttyout(m->morse);
471			} else
472				show(m->morse);
473		}
474	}
475}
476
477void
478show(const char *s)
479{
480	if (lflag) {
481		printf("%s ", s);
482	} else if (sflag) {
483		printf(" %s\n", s);
484	} else {
485		for (; *s; ++s)
486			printf(" %s", *s == '.' ? "dit" : "dah");
487		printf("\n");
488	}
489}
490
491void
492play(const char *s)
493{
494#ifdef SPEAKER
495	const char *c;
496
497	for (c = s; *c != '\0'; c++) {
498		switch (*c) {
499		case '.':
500			sound.frequency = freq;
501			sound.duration = dot_clock;
502			break;
503		case '-':
504			sound.frequency = freq;
505			sound.duration = dot_clock * DASH_LEN;
506			break;
507		case ' ':
508			sound.frequency = 0;
509			sound.duration = cdot_clock * WORD_SPACE;
510			break;
511		default:
512			sound.duration = 0;
513		}
514		if (sound.duration) {
515			if (ioctl(spkr, SPKRTONE, &sound) == -1) {
516				perror("ioctl play");
517				exit(1);
518			}
519		}
520		sound.frequency = 0;
521		sound.duration = dot_clock;
522		if (ioctl(spkr, SPKRTONE, &sound) == -1) {
523			perror("ioctl rest");
524			exit(1);
525		}
526	}
527	sound.frequency = 0;
528	sound.duration = cdot_clock * CHAR_SPACE;
529	ioctl(spkr, SPKRTONE, &sound);
530#endif
531}
532
533void
534ttyout(const char *s)
535{
536	const char *c;
537	int duration, on, lflags;
538
539	for (c = s; *c != '\0'; c++) {
540		switch (*c) {
541		case '.':
542			on = 1;
543			duration = dot_clock;
544			break;
545		case '-':
546			on = 1;
547			duration = dot_clock * DASH_LEN;
548			break;
549		case ' ':
550			on = 0;
551			duration = cdot_clock * WORD_SPACE;
552			break;
553		default:
554			on = 0;
555			duration = 0;
556		}
557		if (on) {
558			ioctl(line, TIOCMGET, &lflags);
559			lflags |= TIOCM_RTS;
560			ioctl(line, TIOCMSET, &lflags);
561		}
562		duration *= 10000;
563		if (duration)
564			usleep(duration);
565		ioctl(line, TIOCMGET, &lflags);
566		lflags &= ~TIOCM_RTS;
567		ioctl(line, TIOCMSET, &lflags);
568		duration = dot_clock * 10000;
569		usleep(duration);
570	}
571	duration = cdot_clock * CHAR_SPACE * 10000;
572	usleep(duration);
573}
574
575void
576sighandler(int signo)
577{
578
579	ioctl(line, TIOCMSET, &olflags);
580	tcsetattr(line, TCSANOW, &otty);
581
582	signal(signo, SIG_DFL);
583	(void)kill(getpid(), signo);
584}
585