morse.c revision 126044
1114987Speter/*
2114987Speter * Copyright (c) 1988, 1993
3114987Speter *	The Regents of the University of California.  All rights reserved.
4114987Speter *
5114987Speter * Redistribution and use in source and binary forms, with or without
6114987Speter * modification, are permitted provided that the following conditions
7114987Speter * are met:
8114987Speter * 1. Redistributions of source code must retain the above copyright
9114987Speter *    notice, this list of conditions and the following disclaimer.
10114987Speter * 2. Redistributions in binary form must reproduce the above copyright
11114987Speter *    notice, this list of conditions and the following disclaimer in the
12114987Speter *    documentation and/or other materials provided with the distribution.
13114987Speter * 3. All advertising materials mentioning features or use of this software
14114987Speter *    must display the following acknowledgement:
15114987Speter *	This product includes software developed by the University of
16114987Speter *	California, Berkeley and its contributors.
17114987Speter * 4. Neither the name of the University nor the names of its contributors
18114987Speter *    may be used to endorse or promote products derived from this software
19114987Speter *    without specific prior written permission.
20114987Speter *
21114987Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22114987Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23114987Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24114987Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25114987Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26114987Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27114987Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28114987Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29114987Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30114987Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31114987Speter * SUCH DAMAGE.
32114987Speter */
33114987Speter
34114987Speter/*
35114987Speter * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
36114987Speter * <lyndon@orthanc.com>
37114987Speter */
38118031Sobrien
39118031Sobrien#ifndef lint
40118031Sobrienstatic const char copyright[] =
41114987Speter"@(#) Copyright (c) 1988, 1993\n\
42114987Speter	The Regents of the University of California.  All rights reserved.\n";
43114987Speter#endif /* not lint */
44114987Speter
45114987Speter#ifndef lint
46114987Speter#if 0
47114987Speterstatic char sccsid[] = "@(#)morse.c	8.1 (Berkeley) 5/31/93";
48114987Speter#endif
49114987Speterstatic const char rcsid[] =
50114987Speter "$FreeBSD: head/games/morse/morse.c 126044 2004-02-20 13:46:39Z fanf $";
51114987Speter#endif /* not lint */
52114987Speter
53114987Speter#include <sys/time.h>
54114987Speter
55114987Speter#include <ctype.h>
56114987Speter#include <fcntl.h>
57114987Speter#include <langinfo.h>
58114987Speter#include <locale.h>
59114987Speter#include <signal.h>
60114987Speter#include <stdio.h>
61114987Speter#include <stdlib.h>
62114987Speter#include <string.h>
63114987Speter#include <termios.h>
64114987Speter#include <unistd.h>
65114987Speter
66114987Speter#ifdef SPEAKER
67114987Speter#include <machine/speaker.h>
68114987Speter#endif
69114987Speter
70114987Speterstruct morsetab {
71114987Speter	char            inchar;
72114987Speter	char           *morse;
73114987Speter};
74114987Speter
75114987Speterstatic const struct morsetab mtab[] = {
76114987Speter
77114987Speter	/* letters */
78114987Speter
79122849Speter	{'a', ".-"},
80114987Speter	{'b', "-..."},
81114987Speter	{'c', "-.-."},
82114987Speter	{'d', "-.."},
83114987Speter	{'e', "."},
84114987Speter	{'f', "..-."},
85119336Speter	{'g', "--."},
86114987Speter	{'h', "...."},
87114987Speter	{'i', ".."},
88114987Speter	{'j', ".---"},
89114987Speter	{'k', "-.-"},
90114987Speter	{'l', ".-.."},
91114987Speter	{'m', "--"},
92114987Speter	{'n', "-."},
93114987Speter	{'o', "---"},
94114987Speter	{'p', ".--."},
95114987Speter	{'q', "--.-"},
96114987Speter	{'r', ".-."},
97114987Speter	{'s', "..."},
98114987Speter	{'t', "-"},
99114987Speter	{'u', "..-"},
100114987Speter	{'v', "...-"},
101114987Speter	{'w', ".--"},
102114987Speter	{'x', "-..-"},
103114987Speter	{'y', "-.--"},
104114987Speter	{'z', "--.."},
105114987Speter
106114987Speter	/* digits */
107114987Speter
108114987Speter	{'0', "-----"},
109114987Speter	{'1', ".----"},
110114987Speter	{'2', "..---"},
111114987Speter	{'3', "...--"},
112114987Speter	{'4', "....-"},
113114987Speter	{'5', "....."},
114114987Speter	{'6', "-...."},
115114987Speter	{'7', "--..."},
116114987Speter	{'8', "---.."},
117114987Speter	{'9', "----."},
118114987Speter
119114987Speter	/* punctuation */
120114987Speter
121114987Speter	{',', "--..--"},
122114987Speter	{'.', ".-.-.-"},
123114987Speter	{'"', ".-..-."},
124114987Speter	{'!', "..--."},
125114987Speter	{'?', "..--.."},
126114987Speter	{'/', "-..-."},
127114987Speter	{'-', "-....-"},
128114987Speter	{'=', "-...-"},		/* BT */
129114987Speter	{':', "---..."},
130114987Speter	{';', "-.-.-."},
131114987Speter	{'(', "-.--."},		/* KN */
132114987Speter	{')', "-.--.-"},
133114987Speter	{'$', "...-..-"},
134114987Speter	{'+', ".-.-."},		/* AR */
135114987Speter	{'@', ".--.-."},	/* AC */
136114987Speter
137114987Speter	/* prosigns without already assigned values */
138114987Speter
139114987Speter	{'#', ".-..."},		/* AS */
140114987Speter	{'&', "...-.-"},	/* SK */
141114987Speter	{'*', "...-."},		/* VE */
142114987Speter	{'%', "-...-.-"},	/* BK */
143114987Speter
144114987Speter	{'\0', ""}
145114987Speter};
146114987Speter
147114987Speter
148114987Speterstatic const struct morsetab iso8859tab[] = {
149114987Speter	{'�', ".--.-"},
150114987Speter	{'�', ".--.-"},
151114987Speter	{'�', ".--.-"},
152114987Speter	{'�', ".-.-"},
153114987Speter	{'�', "-.-.."},
154114987Speter	{'�', "..-.."},
155114987Speter	{'�', "..-.."},
156114987Speter	{'�', "-..-."},
157114987Speter	{'�', "---."},
158114987Speter	{'�', "..--"},
159114987Speter
160114987Speter	{'\0', ""}
161114987Speter};
162114987Speter
163114987Speterstatic const struct morsetab koi8rtab[] = {
164114987Speter	/*
165114987Speter	 * the cyrillic alphabet; you'll need a KOI8R font in order
166114987Speter	 * to see the actual characters
167114987Speter	 */
168114987Speter	{'�', ".-"},		/* a */
169114987Speter	{'�', "-..."},	/* be */
170114987Speter	{'�', ".--"},	/* ve */
171114987Speter	{'�', "--."},	/* ge */
172114987Speter	{'�', "-.."},	/* de */
173114987Speter	{'�', "."},		/* ye */
174114987Speter	{'�', "."},         	/* yo, the same as ye */
175114987Speter	{'�', "...-"},	/* she */
176114987Speter	{'�', "--.."},	/* ze */
177114987Speter	{'�', ".."},		/* i */
178114987Speter	{'�', ".---"},	/* i kratkoye */
179114987Speter	{'�', "-.-"},	/* ka */
180114987Speter	{'�', ".-.."},	/* el */
181114987Speter	{'�', "--"},		/* em */
182114987Speter	{'�', "-."},		/* en */
183114987Speter	{'�', "---"},	/* o */
184114987Speter	{'�', ".--."},	/* pe */
185114987Speter	{'�', ".-."},	/* er */
186114987Speter	{'�', "..."},	/* es */
187114987Speter	{'�', "-"},		/* te */
188114987Speter	{'�', "..-"},	/* u */
189114987Speter	{'�', "..-."},	/* ef */
190114987Speter	{'�', "...."},	/* kha */
191114987Speter	{'�', "-.-."},	/* ce */
192114987Speter	{'�', "---."},	/* che */
193114987Speter	{'�', "----"},	/* sha */
194114987Speter	{'�', "--.-"},	/* shcha */
195114987Speter	{'�', "-.--"},	/* yi */
196114987Speter	{'�', "-..-"},	/* myakhkij znak */
197114987Speter	{'�', "..-.."},	/* ae */
198114987Speter	{'�', "..--"},	/* yu */
199114987Speter	{'�', ".-.-"},	/* ya */
200114987Speter
201114987Speter	{'\0', ""}
202114987Speter};
203114987Speter
204114987Spetervoid            show(const char *), play(const char *), morse(char);
205114987Spetervoid		ttyout(const char *);
206114987Spetervoid		sighandler(int);
207114987Speter
208114987Speter#define GETOPTOPTS "d:ef:lsw:"
209114987Speter#define USAGE \
210114987Speter"usage: morse [-els] [-d device] [-w speed] [-f frequency] [string ...]\n"
211114987Speter
212114987Speterstatic int      pflag, lflag, sflag, eflag;
213114987Speterstatic int      wpm = 20;	/* words per minute */
214114987Speter#define FREQUENCY 600
215114987Speterstatic int      freq = FREQUENCY;
216114987Speterstatic char	*device;	/* for tty-controlled generator */
217114987Speter
218114987Speter#define DASH_LEN 3
219114987Speter#define CHAR_SPACE 3
220114987Speter#define WORD_SPACE (7 - CHAR_SPACE - 1)
221114987Speterstatic float    dot_clock;
222114987Speterint             spkr, line;
223114987Speterstruct termios	otty, ntty;
224114987Speterint		olflags;
225114987Speter
226114987Speter#ifdef SPEAKER
227114987Spetertone_t          sound;
228114987Speter#undef GETOPTOPTS
229114987Speter#define GETOPTOPTS "d:ef:lpsw:"
230114987Speter#undef USAGE
231114987Speter#define USAGE \
232114987Speter"usage: morse [-s] [-p] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
233114987Speter#endif
234114987Speter
235114987Speterstatic const struct morsetab *hightab;
236114987Speter
237114987Speterint
238114987Spetermain(int argc, char **argv)
239114987Speter{
240114987Speter	int    ch, lflags;
241114987Speter	char  *p, *codeset;
242114987Speter
243114987Speter	while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
244114987Speter		switch ((char) ch) {
245114987Speter		case 'd':
246114987Speter			device = optarg;
247114987Speter			break;
248114987Speter		case 'e':
249114987Speter			eflag = 1;
250114987Speter			setvbuf(stdout, 0, _IONBF, 0);
251114987Speter			break;
252114987Speter		case 'f':
253114987Speter			freq = atoi(optarg);
254119336Speter			break;
255114987Speter		case 'l':
256114987Speter			lflag = 1;
257114987Speter			break;
258114987Speter#ifdef SPEAKER
259114987Speter		case 'p':
260114987Speter			pflag = 1;
261114987Speter			break;
262114987Speter#endif
263114987Speter		case 's':
264120346Speter			sflag = 1;
265114987Speter			break;
266114987Speter		case 'w':
267114987Speter			wpm = atoi(optarg);
268114987Speter			break;
269114987Speter		case '?':
270114987Speter		default:
271120346Speter			fputs(USAGE, stderr);
272114987Speter			exit(1);
273114987Speter		}
274114987Speter	if (sflag && lflag) {
275114987Speter		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