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