morse.c revision 78793
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 78793 2001-06-26 01:43:52Z ache $";
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:sw:"
206#define USAGE \
207"usage: morse [-s] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
208
209static int      pflag, 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:psw:"
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#ifdef SPEAKER
253		case 'p':
254			pflag = 1;
255			break;
256#endif
257		case 's':
258			sflag = 1;
259			break;
260		case 'w':
261			wpm = atoi(optarg);
262			break;
263		case '?':
264		default:
265			fputs(USAGE, stderr);
266			exit(1);
267		}
268	if ((pflag || device) && sflag) {
269		fputs("morse: only one of -p, -d and -s allowed\n", stderr);
270		exit(1);
271	}
272	if ((pflag || device) && ((wpm < 1) || (wpm > 60))) {
273		fputs("morse: insane speed\n", stderr);
274		exit(1);
275	}
276	if ((pflag || device) && (freq == 0))
277		freq = FREQUENCY;
278
279#ifdef SPEAKER
280	if (pflag) {
281		if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
282			perror(SPEAKER);
283			exit(1);
284		}
285	} else
286#endif
287	if (device) {
288		if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
289			perror("open tty line");
290			exit(1);
291		}
292		if (tcgetattr(line, &otty) == -1) {
293			perror("tcgetattr() failed");
294			exit(1);
295		}
296		ntty = otty;
297		ntty.c_cflag |= CLOCAL;
298		tcsetattr(line, TCSANOW, &ntty);
299		lflags = fcntl(line, F_GETFL);
300		lflags &= ~O_NONBLOCK;
301		fcntl(line, F_SETFL, &lflags);
302		ioctl(line, TIOCMGET, &lflags);
303		lflags &= ~TIOCM_RTS;
304		olflags = lflags;
305		ioctl(line, TIOCMSET, &lflags);
306		(void)signal(SIGHUP, sighandler);
307		(void)signal(SIGINT, sighandler);
308		(void)signal(SIGQUIT, sighandler);
309		(void)signal(SIGTERM, sighandler);
310	}
311	if (pflag || device) {
312		dot_clock = wpm / 2.4;		/* dots/sec */
313		dot_clock = 1 / dot_clock;	/* duration of a dot */
314		dot_clock = dot_clock / 2;	/* dot_clock runs at twice */
315						/* the dot rate */
316		dot_clock = dot_clock * 100;	/* scale for ioctl */
317	}
318
319	argc -= optind;
320	argv += optind;
321
322	if (setlocale(LC_CTYPE, "") != NULL &&
323	    *(codeset = nl_langinfo(CODESET)) != '\0') {
324		if (strcmp(codeset, "KOI8-R") == 0)
325			hightab = koi8rtab;
326		else if (strcmp(codeset, "ISO8859-1") == 0 ||
327			 strcmp(codeset, "ISO8859-15") == 0)
328			hightab = iso8859tab;
329	}
330
331	if (*argv) {
332		do {
333			for (p = *argv; *p; ++p) {
334				if (eflag)
335					putchar(*p);
336				morse(*p);
337			}
338			if (eflag)
339				putchar(' ');
340			morse(' ');
341		} while (*++argv);
342	} else {
343		while ((ch = getchar()) != EOF) {
344			if (eflag)
345				putchar(ch);
346			morse(ch);
347		}
348	}
349	if (device)
350		tcsetattr(line, TCSANOW, &otty);
351	exit(0);
352}
353
354void
355morse(char c)
356{
357	const struct morsetab *m;
358
359	if (isalpha((unsigned char)c))
360		c = tolower((unsigned char)c);
361	if ((c == '\r') || (c == '\n'))
362		c = ' ';
363	if (c == ' ') {
364		if (pflag) {
365			play(" ");
366			return;
367		} else if (device) {
368			ttyout(" ");
369			return;
370		} else {
371			show("");
372			return;
373		}
374	}
375	for (m = ((unsigned char)c < 0x80? mtab: hightab);
376	     m != NULL && m->inchar != '\0';
377	     m++) {
378		if (m->inchar == c) {
379			if (pflag) {
380				play(m->morse);
381			} else if (device) {
382				ttyout(m->morse);
383			} else
384				show(m->morse);
385		}
386	}
387}
388
389void
390show(const char *s)
391{
392	if (sflag)
393		printf(" %s", s);
394	else
395		for (; *s; ++s)
396			printf(" %s", *s == '.' ? "dit" : "dah");
397	printf("\n");
398}
399
400void
401play(const char *s)
402{
403#ifdef SPEAKER
404	const char *c;
405
406	for (c = s; *c != '\0'; c++) {
407		switch (*c) {
408		case '.':
409			sound.frequency = freq;
410			sound.duration = dot_clock;
411			break;
412		case '-':
413			sound.frequency = freq;
414			sound.duration = dot_clock * DASH_LEN;
415			break;
416		case ' ':
417			sound.frequency = 0;
418			sound.duration = dot_clock * WORD_SPACE;
419			break;
420		default:
421			sound.duration = 0;
422		}
423		if (sound.duration) {
424			if (ioctl(spkr, SPKRTONE, &sound) == -1) {
425				perror("ioctl play");
426				exit(1);
427			}
428		}
429		sound.frequency = 0;
430		sound.duration = dot_clock;
431		if (ioctl(spkr, SPKRTONE, &sound) == -1) {
432			perror("ioctl rest");
433			exit(1);
434		}
435	}
436	sound.frequency = 0;
437	sound.duration = dot_clock * CHAR_SPACE;
438	ioctl(spkr, SPKRTONE, &sound);
439#endif
440}
441
442void
443ttyout(const char *s)
444{
445	const char *c;
446	int duration, on, lflags;
447
448	for (c = s; *c != '\0'; c++) {
449		switch (*c) {
450		case '.':
451			on = 1;
452			duration = dot_clock;
453			break;
454		case '-':
455			on = 1;
456			duration = dot_clock * DASH_LEN;
457			break;
458		case ' ':
459			on = 0;
460			duration = dot_clock * WORD_SPACE;
461			break;
462		default:
463			on = 0;
464			duration = 0;
465		}
466		if (on) {
467			ioctl(line, TIOCMGET, &lflags);
468			lflags |= TIOCM_RTS;
469			ioctl(line, TIOCMSET, &lflags);
470		}
471		duration *= 10000;
472		if (duration)
473			usleep(duration);
474		ioctl(line, TIOCMGET, &lflags);
475		lflags &= ~TIOCM_RTS;
476		ioctl(line, TIOCMSET, &lflags);
477		duration = dot_clock * 10000;
478		usleep(duration);
479	}
480	duration = dot_clock * CHAR_SPACE * 10000;
481	usleep(duration);
482}
483
484void
485sighandler(int signo)
486{
487
488	ioctl(line, TIOCMSET, &olflags);
489	tcsetattr(line, TCSANOW, &otty);
490
491	signal(signo, SIG_DFL);
492	(void)kill(getpid(), signo);
493}
494