morse.c revision 203932
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
30/*
31 * Taught to send *real* morse by Lyndon Nerenberg (VE6BBM)
32 * <lyndon@orthanc.ca>
33 */
34
35#ifndef lint
36static const char copyright[] =
37"@(#) Copyright (c) 1988, 1993\n\
38	The Regents of the University of California.  All rights reserved.\n";
39#endif /* not lint */
40
41#ifndef lint
42#if 0
43static char sccsid[] = "@(#)morse.c	8.1 (Berkeley) 5/31/93";
44#endif
45static const char rcsid[] =
46 "$FreeBSD: head/games/morse/morse.c 203932 2010-02-15 18:46:02Z imp $";
47#endif /* not lint */
48
49#include <sys/time.h>
50
51#include <ctype.h>
52#include <fcntl.h>
53#include <langinfo.h>
54#include <locale.h>
55#include <signal.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <termios.h>
60#include <unistd.h>
61
62/* Always use the speaker, let the open fail if -p is selected */
63#define SPEAKER "/dev/speaker"
64
65#ifdef SPEAKER
66#include <dev/speaker/speaker.h>
67#endif
68
69struct morsetab {
70	const char      inchar;
71	const char     *morse;
72};
73
74static const struct morsetab mtab[] = {
75
76	/* letters */
77
78	{'a', ".-"},
79	{'b', "-..."},
80	{'c', "-.-."},
81	{'d', "-.."},
82	{'e', "."},
83	{'f', "..-."},
84	{'g', "--."},
85	{'h', "...."},
86	{'i', ".."},
87	{'j', ".---"},
88	{'k', "-.-"},
89	{'l', ".-.."},
90	{'m', "--"},
91	{'n', "-."},
92	{'o', "---"},
93	{'p', ".--."},
94	{'q', "--.-"},
95	{'r', ".-."},
96	{'s', "..."},
97	{'t', "-"},
98	{'u', "..-"},
99	{'v', "...-"},
100	{'w', ".--"},
101	{'x', "-..-"},
102	{'y', "-.--"},
103	{'z', "--.."},
104
105	/* digits */
106
107	{'0', "-----"},
108	{'1', ".----"},
109	{'2', "..---"},
110	{'3', "...--"},
111	{'4', "....-"},
112	{'5', "....."},
113	{'6', "-...."},
114	{'7', "--..."},
115	{'8', "---.."},
116	{'9', "----."},
117
118	/* punctuation */
119
120	{',', "--..--"},
121	{'.', ".-.-.-"},
122	{'"', ".-..-."},
123	{'!', "..--."},
124	{'?', "..--.."},
125	{'/', "-..-."},
126	{'-', "-....-"},
127	{'=', "-...-"},		/* BT */
128	{':', "---..."},
129	{';', "-.-.-."},
130	{'(', "-.--."},		/* KN */
131	{')', "-.--.-"},
132	{'$', "...-..-"},
133	{'+', ".-.-."},		/* AR */
134	{'@', ".--.-."},	/* AC */
135
136	/* prosigns without already assigned values */
137
138	{'#', ".-..."},		/* AS */
139	{'&', "...-.-"},	/* SK */
140	{'*', "...-."},		/* VE */
141	{'%', "-...-.-"},	/* BK */
142
143	{'\0', ""}
144};
145
146
147static const struct morsetab iso8859_1tab[] = {
148	{'�', ".--.-"},
149	{'�', ".--.-"},
150	{'�', ".--.-"},
151	{'�', ".-.-"},
152	{'�', "-.-.."},
153	{'�', "..-.."},
154	{'�', "..-.."},
155	{'�', "-..-."},
156	{'�', "---."},
157	{'�', "..--"},
158
159	{'\0', ""}
160};
161
162static const struct morsetab iso8859_7tab[] = {
163	/*
164	 * The greek alphabet; you'll need an 8859-7 font in order
165	 * to see the actual characters.
166	 * This table does not implement:
167	 * - the special sequences for the seven diphthongs,
168	 * - the punctuation differences.
169	 * Implementing these features would introduce too many
170	 * special-cases in the program's main loop.
171	 * The diphtong sequences are:
172	 * alpha iota		.-.-
173	 * alpha upsilon	..--
174	 * epsilon upsilon	---.
175	 * eta upsilon		...-
176	 * omikron iota		---..
177	 * omikron upsilon	..-
178	 * upsilon iota		.---
179	 * The different punctuation symbols are:
180	 * ;	..-.-
181	 * !	--..--
182	 */
183	{'�', ".-"},	/* alpha */
184	{'�', ".-"},	/* alpha with acute */
185	{'�', "-..."},	/* beta */
186	{'�', "--."},	/* gamma */
187	{'�', "-.."},	/* delta */
188	{'�', "."},	/* epsilon */
189	{'�', "."},	/* epsilon with acute */
190	{'�', "--.."},	/* zeta */
191	{'�', "...."},	/* eta */
192	{'�', "...."},	/* eta with acute */
193	{'�', "-.-."},	/* theta */
194	{'�', ".."},	/* iota */
195	{'�', ".."},	/* iota with acute */
196	{'�', ".."},	/* iota with diairesis */
197	{'�', ".."},	/* iota with acute and diairesis */
198	{'�', "-.-"},	/* kappa */
199	{'�', ".-.."},	/* lamda */
200	{'�', "--"},	/* mu */
201	{'�', "-."},	/* nu */
202	{'�', "-..-"},	/* xi */
203	{'�', "---"},	/* omicron */
204	{'�', "---"},	/* omicron with acute */
205	{'�', ".--."},	/* pi */
206	{'�', ".-."},	/* rho */
207	{'�', "..."},	/* sigma */
208	{'�', "..."},	/* final sigma */
209	{'�', "-"},	/* tau */
210	{'�', "-.--"},	/* upsilon */
211	{'�', "-.--"},	/* upsilon with acute */
212	{'�', "-.--"},	/* upsilon and diairesis */
213	{'�', "-.--"},	/* upsilon with acute and diairesis */
214	{'�', "..-."},	/* phi */
215	{'�', "----"},	/* chi */
216	{'�', "--.-"},	/* psi */
217	{'�', ".--"},	/* omega */
218	{'�', ".--"},	/* omega with acute */
219
220	{'\0', ""}
221};
222
223static const struct morsetab koi8rtab[] = {
224	/*
225	 * the cyrillic alphabet; you'll need a KOI8R font in order
226	 * to see the actual characters
227	 */
228	{'�', ".-"},		/* a */
229	{'�', "-..."},	/* be */
230	{'�', ".--"},	/* ve */
231	{'�', "--."},	/* ge */
232	{'�', "-.."},	/* de */
233	{'�', "."},		/* ye */
234	{'�', "."},         	/* yo, the same as ye */
235	{'�', "...-"},	/* she */
236	{'�', "--.."},	/* ze */
237	{'�', ".."},		/* i */
238	{'�', ".---"},	/* i kratkoye */
239	{'�', "-.-"},	/* ka */
240	{'�', ".-.."},	/* el */
241	{'�', "--"},		/* em */
242	{'�', "-."},		/* en */
243	{'�', "---"},	/* o */
244	{'�', ".--."},	/* pe */
245	{'�', ".-."},	/* er */
246	{'�', "..."},	/* es */
247	{'�', "-"},		/* te */
248	{'�', "..-"},	/* u */
249	{'�', "..-."},	/* ef */
250	{'�', "...."},	/* kha */
251	{'�', "-.-."},	/* ce */
252	{'�', "---."},	/* che */
253	{'�', "----"},	/* sha */
254	{'�', "--.-"},	/* shcha */
255	{'�', "-.--"},	/* yi */
256	{'�', "-..-"},	/* myakhkij znak */
257	{'�', "..-.."},	/* ae */
258	{'�', "..--"},	/* yu */
259	{'�', ".-.-"},	/* ya */
260
261	{'\0', ""}
262};
263
264void            show(const char *), play(const char *), morse(char);
265void		ttyout(const char *);
266void		sighandler(int);
267
268#define GETOPTOPTS "c:d:ef:lsw:"
269#define USAGE \
270"usage: morse [-els] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
271
272static int      pflag, lflag, sflag, eflag;
273static int      wpm = 20;	/* effective words per minute */
274static int      cpm;		/* effective words per minute between
275				 * characters */
276#define FREQUENCY 600
277static int      freq = FREQUENCY;
278static char	*device;	/* for tty-controlled generator */
279
280#define DASH_LEN 3
281#define CHAR_SPACE 3
282#define WORD_SPACE (7 - CHAR_SPACE - 1)
283static float    dot_clock;
284static float    cdot_clock;
285int             spkr, line;
286struct termios	otty, ntty;
287int		olflags;
288
289#ifdef SPEAKER
290tone_t          sound;
291#undef GETOPTOPTS
292#define GETOPTOPTS "c:d:ef:lpsw:"
293#undef USAGE
294#define USAGE \
295"usage: morse [-elps] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
296#endif
297
298static const struct morsetab *hightab;
299
300int
301main(int argc, char **argv)
302{
303	int    ch, lflags;
304	char  *p, *codeset;
305
306	while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
307		switch ((char) ch) {
308 		case 'c':
309 			cpm = atoi(optarg);
310 			break;
311		case 'd':
312			device = optarg;
313			break;
314		case 'e':
315			eflag = 1;
316			setvbuf(stdout, 0, _IONBF, 0);
317			break;
318		case 'f':
319			freq = atoi(optarg);
320			break;
321		case 'l':
322			lflag = 1;
323			break;
324#ifdef SPEAKER
325		case 'p':
326			pflag = 1;
327			break;
328#endif
329		case 's':
330			sflag = 1;
331			break;
332		case 'w':
333			wpm = atoi(optarg);
334			break;
335		case '?':
336		default:
337			fputs(USAGE, stderr);
338			exit(1);
339		}
340	if (sflag && lflag) {
341		fputs("morse: only one of -l and -s allowed\n", stderr);
342		exit(1);
343	}
344	if ((pflag || device) && (sflag || lflag)) {
345		fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr);
346		exit(1);
347	}
348	if (cpm == 0)
349		cpm = wpm;
350	if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) {
351		fputs("morse: insane speed\n", stderr);
352		exit(1);
353	}
354	if ((pflag || device) && (freq == 0))
355		freq = FREQUENCY;
356
357#ifdef SPEAKER
358	if (pflag) {
359		if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
360			perror(SPEAKER);
361			exit(1);
362		}
363	} else
364#endif
365	if (device) {
366		if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
367			perror("open tty line");
368			exit(1);
369		}
370		if (tcgetattr(line, &otty) == -1) {
371			perror("tcgetattr() failed");
372			exit(1);
373		}
374		ntty = otty;
375		ntty.c_cflag |= CLOCAL;
376		tcsetattr(line, TCSANOW, &ntty);
377		lflags = fcntl(line, F_GETFL);
378		lflags &= ~O_NONBLOCK;
379		fcntl(line, F_SETFL, &lflags);
380		ioctl(line, TIOCMGET, &lflags);
381		lflags &= ~TIOCM_RTS;
382		olflags = lflags;
383		ioctl(line, TIOCMSET, &lflags);
384		(void)signal(SIGHUP, sighandler);
385		(void)signal(SIGINT, sighandler);
386		(void)signal(SIGQUIT, sighandler);
387		(void)signal(SIGTERM, sighandler);
388	}
389	if (pflag || device) {
390		dot_clock = wpm / 2.4;		/* dots/sec */
391		dot_clock = 1 / dot_clock;	/* duration of a dot */
392		dot_clock = dot_clock / 2;	/* dot_clock runs at twice */
393						/* the dot rate */
394		dot_clock = dot_clock * 100;	/* scale for ioctl */
395
396		cdot_clock = cpm / 2.4;		/* dots/sec */
397		cdot_clock = 1 / cdot_clock;	/* duration of a dot */
398		cdot_clock = cdot_clock / 2;	/* dot_clock runs at twice */
399						/* the dot rate */
400		cdot_clock = cdot_clock * 100;	/* scale for ioctl */
401	}
402
403	argc -= optind;
404	argv += optind;
405
406	if (setlocale(LC_CTYPE, "") != NULL &&
407	    *(codeset = nl_langinfo(CODESET)) != '\0') {
408		if (strcmp(codeset, "KOI8-R") == 0)
409			hightab = koi8rtab;
410		else if (strcmp(codeset, "ISO8859-1") == 0 ||
411			 strcmp(codeset, "ISO8859-15") == 0)
412			hightab = iso8859_1tab;
413		else if (strcmp(codeset, "ISO8859-7") == 0)
414			hightab = iso8859_7tab;
415	}
416
417	if (lflag)
418		printf("m");
419	if (*argv) {
420		do {
421			for (p = *argv; *p; ++p) {
422				if (eflag)
423					putchar(*p);
424				morse(*p);
425			}
426			if (eflag)
427				putchar(' ');
428			morse(' ');
429		} while (*++argv);
430	} else {
431		while ((ch = getchar()) != EOF) {
432			if (eflag)
433				putchar(ch);
434			morse(ch);
435		}
436	}
437	if (device)
438		tcsetattr(line, TCSANOW, &otty);
439	exit(0);
440}
441
442void
443morse(char c)
444{
445	const struct morsetab *m;
446
447	if (isalpha((unsigned char)c))
448		c = tolower((unsigned char)c);
449	if ((c == '\r') || (c == '\n'))
450		c = ' ';
451	if (c == ' ') {
452		if (pflag)
453			play(" ");
454		else if (device)
455			ttyout(" ");
456		else if (lflag)
457			printf("\n");
458		else
459			show("");
460		return;
461	}
462	for (m = ((unsigned char)c < 0x80? mtab: hightab);
463	     m != NULL && m->inchar != '\0';
464	     m++) {
465		if (m->inchar == c) {
466			if (pflag) {
467				play(m->morse);
468			} else if (device) {
469				ttyout(m->morse);
470			} else
471				show(m->morse);
472		}
473	}
474}
475
476void
477show(const char *s)
478{
479	if (lflag) {
480		printf("%s ", s);
481	} else if (sflag) {
482		printf(" %s\n", s);
483	} else {
484		for (; *s; ++s)
485			printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" :
486			    "di" : "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