morse.c revision 73349
1159720Syar/*
2159720Syar * Copyright (c) 1988, 1993
3159720Syar *	The Regents of the University of California.  All rights reserved.
4159720Syar *
5159720Syar * Redistribution and use in source and binary forms, with or without
6159720Syar * modification, are permitted provided that the following conditions
7159720Syar * are met:
8159720Syar * 1. Redistributions of source code must retain the above copyright
9159720Syar *    notice, this list of conditions and the following disclaimer.
10159720Syar * 2. Redistributions in binary form must reproduce the above copyright
11159720Syar *    notice, this list of conditions and the following disclaimer in the
12159720Syar *    documentation and/or other materials provided with the distribution.
13159720Syar * 3. All advertising materials mentioning features or use of this software
14159720Syar *    must display the following acknowledgement:
15159720Syar *	This product includes software developed by the University of
16159720Syar *	California, Berkeley and its contributors.
17159720Syar * 4. Neither the name of the University nor the names of its contributors
18159720Syar *    may be used to endorse or promote products derived from this software
19159720Syar *    without specific prior written permission.
20159720Syar *
21159720Syar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22159720Syar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23159720Syar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24159720Syar * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25159720Syar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26159720Syar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27159720Syar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28159720Syar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29159720Syar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30159720Syar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31159720Syar * SUCH DAMAGE.
32159720Syar */
33159720Syar
34159720Syar/*
35159720Syar * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
36159720Syar * <lyndon@orthanc.com>
37159720Syar */
38159720Syar
39159720Syar#ifndef lint
40159720Syarstatic const char copyright[] =
41159720Syar"@(#) Copyright (c) 1988, 1993\n\
42159720Syar	The Regents of the University of California.  All rights reserved.\n";
43159720Syar#endif /* not lint */
44159720Syar
45159720Syar#ifndef lint
46159720Syar#if 0
47159720Syarstatic char sccsid[] = "@(#)morse.c	8.1 (Berkeley) 5/31/93";
48159720Syar#endif
49159720Syarstatic const char rcsid[] =
50159720Syar "$FreeBSD: head/games/morse/morse.c 73349 2001-03-02 16:52:14Z ru $";
51159720Syar#endif /* not lint */
52159720Syar
53159720Syar#include <sys/time.h>
54159720Syar
55159720Syar#include <ctype.h>
56159720Syar#include <fcntl.h>
57159720Syar#include <locale.h>
58159720Syar#include <signal.h>
59159720Syar#include <stdio.h>
60159720Syar#include <stdlib.h>
61159720Syar#include <string.h>
62159720Syar#include <termios.h>
63159720Syar#include <unistd.h>
64159720Syar
65159720Syar#ifdef SPEAKER
66159720Syar#include <machine/speaker.h>
67159720Syar#endif
68159720Syar
69159720Syarstruct morsetab {
70159720Syar	char            inchar;
71159720Syar	char           *morse;
72159720Syar};
73159720Syar
74159720Syarstatic const struct morsetab mtab[] = {
75159720Syar
76159720Syar	/* letters */
77159720Syar
78159720Syar	{'a', ".-"},
79159720Syar	{'b', "-..."},
80159720Syar	{'c', "-.-."},
81159720Syar	{'d', "-.."},
82159720Syar	{'e', "."},
83159720Syar	{'f', "..-."},
84159720Syar	{'g', "--."},
85159720Syar	{'h', "...."},
86159720Syar	{'i', ".."},
87159720Syar	{'j', ".---"},
88159720Syar	{'k', "-.-"},
89159720Syar	{'l', ".-.."},
90159720Syar	{'m', "--"},
91159720Syar	{'n', "-."},
92159720Syar	{'o', "---"},
93159720Syar	{'p', ".--."},
94159720Syar	{'q', "--.-"},
95159720Syar	{'r', ".-."},
96159720Syar	{'s', "..."},
97159720Syar	{'t', "-"},
98159720Syar	{'u', "..-"},
99159720Syar	{'v', "...-"},
100159720Syar	{'w', ".--"},
101159720Syar	{'x', "-..-"},
102159720Syar	{'y', "-.--"},
103159720Syar	{'z', "--.."},
104159720Syar
105159720Syar	/* digits */
106159720Syar
107159720Syar	{'0', "-----"},
108159720Syar	{'1', ".----"},
109159720Syar	{'2', "..---"},
110159720Syar	{'3', "...--"},
111159720Syar	{'4', "....-"},
112159720Syar	{'5', "....."},
113159720Syar	{'6', "-...."},
114159720Syar	{'7', "--..."},
115159720Syar	{'8', "---.."},
116159720Syar	{'9', "----."},
117159720Syar
118159720Syar	/* punctuation */
119159720Syar
120159720Syar	{',', "--..--"},
121159720Syar	{'.', ".-.-.-"},
122159720Syar	{'?', "..--.."},
123159720Syar	{'/', "-..-."},
124159720Syar	{'-', "-....-"},
125159720Syar	{'=', "-...-"},		/* BT */
126159720Syar	{':', "---..."},
127159720Syar	{';', "-.-.-."},
128159720Syar	{'(', "-.--."},		/* KN */
129	{')', "-.--.-"},
130	{'$', "...-..-"},
131	{'+', ".-.-."},		/* AR */
132
133	/* prosigns without already assigned values */
134
135	{'#', ".-..."},		/* AS */
136	{'@', "...-.-"},	/* SK */
137	{'*', "...-."},		/* VE */
138	{'%', "-...-.-"},	/* BK */
139
140	{'\0', ""}
141};
142
143
144static const struct morsetab iso8859tab[] = {
145	{'�', ".--.-"},
146	{'�', ".--.-"},
147	{'�', ".--.-"},
148	{'�', ".-.-"},
149	{'�', "-.-.."},
150	{'�', "..-.."},
151	{'�', "..-.."},
152	{'�', "-..-."},
153	{'�', "---."},
154	{'�', "..--"},
155
156	{'\0', ""}
157};
158
159static const struct morsetab koi8rtab[] = {
160	/*
161	 * the cyrillic alphabet; you'll need a KOI8R font in order
162	 * to see the actual characters
163	 */
164	{'�', ".-"},		/* a */
165	{'�', "-..."},	/* be */
166	{'�', ".--"},	/* ve */
167	{'�', "--."},	/* ge */
168	{'�', "-.."},	/* de */
169	{'�', "."},		/* ye */
170	{'�', "."},         	/* yo, the same as ye */
171	{'�', "...-"},	/* she */
172	{'�', "--.."},	/* ze */
173	{'�', ".."},		/* i */
174	{'�', ".---"},	/* i kratkoye */
175	{'�', "-.-"},	/* ka */
176	{'�', ".-.."},	/* el */
177	{'�', "--"},		/* em */
178	{'�', "-."},		/* en */
179	{'�', "---"},	/* o */
180	{'�', ".--."},	/* pe */
181	{'�', ".-."},	/* er */
182	{'�', "..."},	/* es */
183	{'�', "-"},		/* te */
184	{'�', "..-"},	/* u */
185	{'�', "..-."},	/* ef */
186	{'�', "...."},	/* kha */
187	{'�', "-.-."},	/* ce */
188	{'�', "---."},	/* che */
189	{'�', "----"},	/* sha */
190	{'�', "--.-"},	/* shcha */
191	{'�', "-.--"},	/* yi */
192	{'�', "-..-"},	/* myakhkij znak */
193	{'�', "..-.."},	/* ae */
194	{'�', "..--"},	/* yu */
195	{'�', ".-.-"},	/* ya */
196
197	{'\0', ""}
198};
199
200void            show(const char *), play(const char *), morse(char);
201void		ttyout(const char *);
202void		sighandler(int);
203
204#define GETOPTOPTS "d:ef:sw:"
205#define USAGE \
206"usage: morse [-s] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
207
208static int      pflag, sflag, eflag;
209static int      wpm = 20;	/* words per minute */
210#define FREQUENCY 600
211static int      freq = FREQUENCY;
212static char	*device;	/* for tty-controlled generator */
213
214#define DASH_LEN 3
215#define CHAR_SPACE 3
216#define WORD_SPACE (7 - CHAR_SPACE - 1)
217static float    dot_clock;
218int             spkr, line;
219struct termios	otty, ntty;
220int		olflags;
221
222#ifdef SPEAKER
223tone_t          sound;
224#undef GETOPTOPTS
225#define GETOPTOPTS "d:ef:psw:"
226#undef USAGE
227#define USAGE \
228"usage: morse [-s] [-p] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
229#endif
230
231static const struct morsetab *hightab = iso8859tab;
232
233int
234main(int argc, char **argv)
235{
236	int    ch, lflags;
237	char  *p;
238
239	while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
240		switch ((char) ch) {
241		case 'd':
242			device = optarg;
243			break;
244		case 'e':
245			eflag = 1;
246			setvbuf(stdout, 0, _IONBF, 0);
247			break;
248		case 'f':
249			freq = atoi(optarg);
250			break;
251#ifdef SPEAKER
252		case 'p':
253			pflag = 1;
254			break;
255#endif
256		case 's':
257			sflag = 1;
258			break;
259		case 'w':
260			wpm = atoi(optarg);
261			break;
262		case '?':
263		default:
264			fputs(USAGE, stderr);
265			exit(1);
266		}
267	if ((pflag || device) && sflag) {
268		fputs("morse: only one of -p, -d and -s allowed\n", stderr);
269		exit(1);
270	}
271	if ((pflag || device) && ((wpm < 1) || (wpm > 60))) {
272		fputs("morse: insane speed\n", stderr);
273		exit(1);
274	}
275	if ((pflag || device) && (freq == 0))
276		freq = FREQUENCY;
277
278#ifdef SPEAKER
279	if (pflag) {
280		if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
281			perror(SPEAKER);
282			exit(1);
283		}
284	} else
285#endif
286	if (device) {
287		if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
288			perror("open tty line");
289			exit(1);
290		}
291		if (tcgetattr(line, &otty) == -1) {
292			perror("tcgetattr() failed");
293			exit(1);
294		}
295		ntty = otty;
296		ntty.c_cflag |= CLOCAL;
297		tcsetattr(line, TCSANOW, &ntty);
298		lflags = fcntl(line, F_GETFL);
299		lflags &= ~O_NONBLOCK;
300		fcntl(line, F_SETFL, &lflags);
301		ioctl(line, TIOCMGET, &lflags);
302		lflags &= ~TIOCM_RTS;
303		olflags = lflags;
304		ioctl(line, TIOCMSET, &lflags);
305		(void)signal(SIGHUP, sighandler);
306		(void)signal(SIGINT, sighandler);
307		(void)signal(SIGQUIT, sighandler);
308		(void)signal(SIGTERM, sighandler);
309	}
310	if (pflag || device) {
311		dot_clock = wpm / 2.4;		/* dots/sec */
312		dot_clock = 1 / dot_clock;	/* duration of a dot */
313		dot_clock = dot_clock / 2;	/* dot_clock runs at twice */
314						/* the dot rate */
315		dot_clock = dot_clock * 100;	/* scale for ioctl */
316	}
317
318	argc -= optind;
319	argv += optind;
320
321	if(((p = getenv("LC_ALL")) && *p) ||
322	   ((p = getenv("LC_CTYPE")) && *p) ||
323	   ((p = getenv("LANG")) && *p)) {
324		if(strlen(p) >= sizeof(".KOI8-R") &&
325		   strcasecmp(&p[strlen(p) + 1 - sizeof(".KOI8-R")], ".KOI8-R") == 0)
326			hightab = koi8rtab;
327	}
328	(void) setlocale(LC_CTYPE, "");
329
330	if (*argv) {
331		do {
332			for (p = *argv; *p; ++p) {
333				if (eflag)
334					putchar(*p);
335				morse(*p);
336			}
337			if (eflag)
338				putchar(' ');
339			morse(' ');
340		} while (*++argv);
341	} else {
342		while ((ch = getchar()) != EOF) {
343			if (eflag)
344				putchar(ch);
345			morse(ch);
346		}
347	}
348	if (device)
349		tcsetattr(line, TCSANOW, &otty);
350	exit(0);
351}
352
353void
354morse(char c)
355{
356	const struct morsetab *m;
357
358	if (isalpha((unsigned char)c))
359		c = tolower((unsigned char)c);
360	if ((c == '\r') || (c == '\n'))
361		c = ' ';
362	if (c == ' ') {
363		if (pflag) {
364			play(" ");
365			return;
366		} else if (device) {
367			ttyout(" ");
368			return;
369		} else {
370			show("");
371			return;
372		}
373	}
374	for (m = ((unsigned char)c < 0x80? mtab: hightab);
375	     m->inchar != '\0';
376	     m++) {
377		if (m->inchar == c) {
378			if (pflag) {
379				play(m->morse);
380			} else if (device) {
381				ttyout(m->morse);
382			} else
383				show(m->morse);
384		}
385	}
386}
387
388void
389show(const char *s)
390{
391	if (sflag)
392		printf(" %s", s);
393	else
394		for (; *s; ++s)
395			printf(" %s", *s == '.' ? "dit" : "dah");
396	printf("\n");
397}
398
399void
400play(const char *s)
401{
402#ifdef SPEAKER
403	const char *c;
404
405	for (c = s; *c != '\0'; c++) {
406		switch (*c) {
407		case '.':
408			sound.frequency = freq;
409			sound.duration = dot_clock;
410			break;
411		case '-':
412			sound.frequency = freq;
413			sound.duration = dot_clock * DASH_LEN;
414			break;
415		case ' ':
416			sound.frequency = 0;
417			sound.duration = dot_clock * WORD_SPACE;
418			break;
419		default:
420			sound.duration = 0;
421		}
422		if (sound.duration) {
423			if (ioctl(spkr, SPKRTONE, &sound) == -1) {
424				perror("ioctl play");
425				exit(1);
426			}
427		}
428		sound.frequency = 0;
429		sound.duration = dot_clock;
430		if (ioctl(spkr, SPKRTONE, &sound) == -1) {
431			perror("ioctl rest");
432			exit(1);
433		}
434	}
435	sound.frequency = 0;
436	sound.duration = dot_clock * CHAR_SPACE;
437	ioctl(spkr, SPKRTONE, &sound);
438#endif
439}
440
441void
442ttyout(const char *s)
443{
444	const char *c;
445	int duration, on, lflags;
446
447	for (c = s; *c != '\0'; c++) {
448		switch (*c) {
449		case '.':
450			on = 1;
451			duration = dot_clock;
452			break;
453		case '-':
454			on = 1;
455			duration = dot_clock * DASH_LEN;
456			break;
457		case ' ':
458			on = 0;
459			duration = dot_clock * WORD_SPACE;
460			break;
461		default:
462			on = 0;
463			duration = 0;
464		}
465		if (on) {
466			ioctl(line, TIOCMGET, &lflags);
467			lflags |= TIOCM_RTS;
468			ioctl(line, TIOCMSET, &lflags);
469		}
470		duration *= 10000;
471		if (duration)
472			usleep(duration);
473		ioctl(line, TIOCMGET, &lflags);
474		lflags &= ~TIOCM_RTS;
475		ioctl(line, TIOCMSET, &lflags);
476		duration = dot_clock * 10000;
477		usleep(duration);
478	}
479	duration = dot_clock * CHAR_SPACE * 10000;
480	usleep(duration);
481}
482
483void
484sighandler(int signo)
485{
486
487	ioctl(line, TIOCMSET, &olflags);
488	tcsetattr(line, TCSANOW, &otty);
489
490	signal(signo, SIG_DFL);
491	(void)kill(getpid(), signo);
492}
493