morse.c revision 126044
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 126044 2004-02-20 13:46:39Z fanf $";
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
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 iso8859tab[] = {
149	{'�', ".--.-"},
150	{'�', ".--.-"},
151	{'�', ".--.-"},
152	{'�', ".-.-"},
153	{'�', "-.-.."},
154	{'�', "..-.."},
155	{'�', "..-.."},
156	{'�', "-..-."},
157	{'�', "---."},
158	{'�', "..--"},
159
160	{'\0', ""}
161};
162
163static const struct morsetab koi8rtab[] = {
164	/*
165	 * the cyrillic alphabet; you'll need a KOI8R font in order
166	 * to see the actual characters
167	 */
168	{'�', ".-"},		/* a */
169	{'�', "-..."},	/* be */
170	{'�', ".--"},	/* ve */
171	{'�', "--."},	/* ge */
172	{'�', "-.."},	/* de */
173	{'�', "."},		/* ye */
174	{'�', "."},         	/* yo, the same as ye */
175	{'�', "...-"},	/* she */
176	{'�', "--.."},	/* ze */
177	{'�', ".."},		/* i */
178	{'�', ".---"},	/* i kratkoye */
179	{'�', "-.-"},	/* ka */
180	{'�', ".-.."},	/* el */
181	{'�', "--"},		/* em */
182	{'�', "-."},		/* en */
183	{'�', "---"},	/* o */
184	{'�', ".--."},	/* pe */
185	{'�', ".-."},	/* er */
186	{'�', "..."},	/* es */
187	{'�', "-"},		/* te */
188	{'�', "..-"},	/* u */
189	{'�', "..-."},	/* ef */
190	{'�', "...."},	/* kha */
191	{'�', "-.-."},	/* ce */
192	{'�', "---."},	/* che */
193	{'�', "----"},	/* sha */
194	{'�', "--.-"},	/* shcha */
195	{'�', "-.--"},	/* yi */
196	{'�', "-..-"},	/* myakhkij znak */
197	{'�', "..-.."},	/* ae */
198	{'�', "..--"},	/* yu */
199	{'�', ".-.-"},	/* ya */
200
201	{'\0', ""}
202};
203
204void            show(const char *), play(const char *), morse(char);
205void		ttyout(const char *);
206void		sighandler(int);
207
208#define GETOPTOPTS "d:ef:lsw:"
209#define USAGE \
210"usage: morse [-els] [-d device] [-w speed] [-f frequency] [string ...]\n"
211
212static int      pflag, lflag, sflag, eflag;
213static int      wpm = 20;	/* words per minute */
214#define FREQUENCY 600
215static int      freq = FREQUENCY;
216static char	*device;	/* for tty-controlled generator */
217
218#define DASH_LEN 3
219#define CHAR_SPACE 3
220#define WORD_SPACE (7 - CHAR_SPACE - 1)
221static float    dot_clock;
222int             spkr, line;
223struct termios	otty, ntty;
224int		olflags;
225
226#ifdef SPEAKER
227tone_t          sound;
228#undef GETOPTOPTS
229#define GETOPTOPTS "d:ef:lpsw:"
230#undef USAGE
231#define USAGE \
232"usage: morse [-s] [-p] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
233#endif
234
235static const struct morsetab *hightab;
236
237int
238main(int argc, char **argv)
239{
240	int    ch, lflags;
241	char  *p, *codeset;
242
243	while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
244		switch ((char) ch) {
245		case 'd':
246			device = optarg;
247			break;
248		case 'e':
249			eflag = 1;
250			setvbuf(stdout, 0, _IONBF, 0);
251			break;
252		case 'f':
253			freq = atoi(optarg);
254			break;
255		case 'l':
256			lflag = 1;
257			break;
258#ifdef SPEAKER
259		case 'p':
260			pflag = 1;
261			break;
262#endif
263		case 's':
264			sflag = 1;
265			break;
266		case 'w':
267			wpm = atoi(optarg);
268			break;
269		case '?':
270		default:
271			fputs(USAGE, stderr);
272			exit(1);
273		}
274	if (sflag && lflag) {
275		fputs("morse: only one of -l and -s allowed\n", stderr);
276		exit(1);
277	}
278	if ((pflag || device) && (sflag || lflag)) {
279		fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr);
280		exit(1);
281	}
282	if ((pflag || device) && ((wpm < 1) || (wpm > 60))) {
283		fputs("morse: insane speed\n", stderr);
284		exit(1);
285	}
286	if ((pflag || device) && (freq == 0))
287		freq = FREQUENCY;
288
289#ifdef SPEAKER
290	if (pflag) {
291		if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
292			perror(SPEAKER);
293			exit(1);
294		}
295	} else
296#endif
297	if (device) {
298		if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
299			perror("open tty line");
300			exit(1);
301		}
302		if (tcgetattr(line, &otty) == -1) {
303			perror("tcgetattr() failed");
304			exit(1);
305		}
306		ntty = otty;
307		ntty.c_cflag |= CLOCAL;
308		tcsetattr(line, TCSANOW, &ntty);
309		lflags = fcntl(line, F_GETFL);
310		lflags &= ~O_NONBLOCK;
311		fcntl(line, F_SETFL, &lflags);
312		ioctl(line, TIOCMGET, &lflags);
313		lflags &= ~TIOCM_RTS;
314		olflags = lflags;
315		ioctl(line, TIOCMSET, &lflags);
316		(void)signal(SIGHUP, sighandler);
317		(void)signal(SIGINT, sighandler);
318		(void)signal(SIGQUIT, sighandler);
319		(void)signal(SIGTERM, sighandler);
320	}
321	if (pflag || device) {
322		dot_clock = wpm / 2.4;		/* dots/sec */
323		dot_clock = 1 / dot_clock;	/* duration of a dot */
324		dot_clock = dot_clock / 2;	/* dot_clock runs at twice */
325						/* the dot rate */
326		dot_clock = dot_clock * 100;	/* scale for ioctl */
327	}
328
329	argc -= optind;
330	argv += optind;
331
332	if (setlocale(LC_CTYPE, "") != NULL &&
333	    *(codeset = nl_langinfo(CODESET)) != '\0') {
334		if (strcmp(codeset, "KOI8-R") == 0)
335			hightab = koi8rtab;
336		else if (strcmp(codeset, "ISO8859-1") == 0 ||
337			 strcmp(codeset, "ISO8859-15") == 0)
338			hightab = iso8859tab;
339	}
340
341	if (lflag)
342		printf("m");
343	if (*argv) {
344		do {
345			for (p = *argv; *p; ++p) {
346				if (eflag)
347					putchar(*p);
348				morse(*p);
349			}
350			if (eflag)
351				putchar(' ');
352			morse(' ');
353		} while (*++argv);
354	} else {
355		while ((ch = getchar()) != EOF) {
356			if (eflag)
357				putchar(ch);
358			morse(ch);
359		}
360	}
361	if (device)
362		tcsetattr(line, TCSANOW, &otty);
363	exit(0);
364}
365
366void
367morse(char c)
368{
369	const struct morsetab *m;
370
371	if (isalpha((unsigned char)c))
372		c = tolower((unsigned char)c);
373	if ((c == '\r') || (c == '\n'))
374		c = ' ';
375	if (c == ' ') {
376		if (pflag)
377			play(" ");
378		else if (device)
379			ttyout(" ");
380		else if (lflag)
381			printf("\n");
382		else
383			show("");
384		return;
385	}
386	for (m = ((unsigned char)c < 0x80? mtab: hightab);
387	     m != NULL && m->inchar != '\0';
388	     m++) {
389		if (m->inchar == c) {
390			if (pflag) {
391				play(m->morse);
392			} else if (device) {
393				ttyout(m->morse);
394			} else
395				show(m->morse);
396		}
397	}
398}
399
400void
401show(const char *s)
402{
403	if (lflag) {
404		printf("%s ", s);
405	} else if (sflag) {
406		printf(" %s\n", s);
407	} else {
408		for (; *s; ++s)
409			printf(" %s", *s == '.' ? "dit" : "dah");
410		printf("\n");
411	}
412}
413
414void
415play(const char *s)
416{
417#ifdef SPEAKER
418	const char *c;
419
420	for (c = s; *c != '\0'; c++) {
421		switch (*c) {
422		case '.':
423			sound.frequency = freq;
424			sound.duration = dot_clock;
425			break;
426		case '-':
427			sound.frequency = freq;
428			sound.duration = dot_clock * DASH_LEN;
429			break;
430		case ' ':
431			sound.frequency = 0;
432			sound.duration = dot_clock * WORD_SPACE;
433			break;
434		default:
435			sound.duration = 0;
436		}
437		if (sound.duration) {
438			if (ioctl(spkr, SPKRTONE, &sound) == -1) {
439				perror("ioctl play");
440				exit(1);
441			}
442		}
443		sound.frequency = 0;
444		sound.duration = dot_clock;
445		if (ioctl(spkr, SPKRTONE, &sound) == -1) {
446			perror("ioctl rest");
447			exit(1);
448		}
449	}
450	sound.frequency = 0;
451	sound.duration = dot_clock * CHAR_SPACE;
452	ioctl(spkr, SPKRTONE, &sound);
453#endif
454}
455
456void
457ttyout(const char *s)
458{
459	const char *c;
460	int duration, on, lflags;
461
462	for (c = s; *c != '\0'; c++) {
463		switch (*c) {
464		case '.':
465			on = 1;
466			duration = dot_clock;
467			break;
468		case '-':
469			on = 1;
470			duration = dot_clock * DASH_LEN;
471			break;
472		case ' ':
473			on = 0;
474			duration = dot_clock * WORD_SPACE;
475			break;
476		default:
477			on = 0;
478			duration = 0;
479		}
480		if (on) {
481			ioctl(line, TIOCMGET, &lflags);
482			lflags |= TIOCM_RTS;
483			ioctl(line, TIOCMSET, &lflags);
484		}
485		duration *= 10000;
486		if (duration)
487			usleep(duration);
488		ioctl(line, TIOCMGET, &lflags);
489		lflags &= ~TIOCM_RTS;
490		ioctl(line, TIOCMSET, &lflags);
491		duration = dot_clock * 10000;
492		usleep(duration);
493	}
494	duration = dot_clock * CHAR_SPACE * 10000;
495	usleep(duration);
496}
497
498void
499sighandler(int signo)
500{
501
502	ioctl(line, TIOCMSET, &olflags);
503	tcsetattr(line, TCSANOW, &otty);
504
505	signal(signo, SIG_DFL);
506	(void)kill(getpid(), signo);
507}
508