morse.c revision 126040
148905Srnordier/*
248905Srnordier * Copyright (c) 1988, 1993
348905Srnordier *	The Regents of the University of California.  All rights reserved.
448905Srnordier *
548905Srnordier * Redistribution and use in source and binary forms, with or without
648905Srnordier * modification, are permitted provided that the following conditions
748905Srnordier * are met:
848905Srnordier * 1. Redistributions of source code must retain the above copyright
948905Srnordier *    notice, this list of conditions and the following disclaimer.
1048905Srnordier * 2. Redistributions in binary form must reproduce the above copyright
1148905Srnordier *    notice, this list of conditions and the following disclaimer in the
1248905Srnordier *    documentation and/or other materials provided with the distribution.
1348905Srnordier * 3. All advertising materials mentioning features or use of this software
1448905Srnordier *    must display the following acknowledgement:
1548905Srnordier *	This product includes software developed by the University of
1648905Srnordier *	California, Berkeley and its contributors.
1748905Srnordier * 4. Neither the name of the University nor the names of its contributors
1848905Srnordier *    may be used to endorse or promote products derived from this software
1948905Srnordier *    without specific prior written permission.
2048905Srnordier *
2148905Srnordier * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2248905Srnordier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2348905Srnordier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2448905Srnordier * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2548905Srnordier * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2650479Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2748905Srnordier * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2848905Srnordier * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2948905Srnordier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3048905Srnordier * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3148905Srnordier * SUCH DAMAGE.
3248905Srnordier */
3348905Srnordier
3448905Srnordier/*
3548905Srnordier * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
3648905Srnordier * <lyndon@orthanc.com>
3748905Srnordier */
3848905Srnordier
3948905Srnordier#ifndef lint
4048905Srnordierstatic const char copyright[] =
4148905Srnordier"@(#) Copyright (c) 1988, 1993\n\
4248905Srnordier	The Regents of the University of California.  All rights reserved.\n";
4348905Srnordier#endif /* not lint */
4448905Srnordier
4548905Srnordier#ifndef lint
4648905Srnordier#if 0
4748905Srnordierstatic char sccsid[] = "@(#)morse.c	8.1 (Berkeley) 5/31/93";
4848905Srnordier#endif
4948905Srnordierstatic const char rcsid[] =
5048905Srnordier "$FreeBSD: head/games/morse/morse.c 126040 2004-02-20 11:55:38Z fanf $";
5148905Srnordier#endif /* not lint */
5248905Srnordier
5348905Srnordier#include <sys/time.h>
5448905Srnordier
5548905Srnordier#include <ctype.h>
5648905Srnordier#include <fcntl.h>
5748905Srnordier#include <langinfo.h>
5848905Srnordier#include <locale.h>
5948905Srnordier#include <signal.h>
6048905Srnordier#include <stdio.h>
6148905Srnordier#include <stdlib.h>
6248905Srnordier#include <string.h>
6348905Srnordier#include <termios.h>
6448905Srnordier#include <unistd.h>
6548905Srnordier
6648905Srnordier#ifdef SPEAKER
6748905Srnordier#include <machine/speaker.h>
6848905Srnordier#endif
6948905Srnordier
7048905Srnordierstruct morsetab {
7148905Srnordier	char            inchar;
7248905Srnordier	char           *morse;
7348905Srnordier};
7448905Srnordier
7548905Srnordierstatic const struct morsetab mtab[] = {
7648905Srnordier
7748905Srnordier	/* letters */
7848905Srnordier
7948905Srnordier	{'a', ".-"},
8048905Srnordier	{'b', "-..."},
8148905Srnordier	{'c', "-.-."},
8248905Srnordier	{'d', "-.."},
8348905Srnordier	{'e', "."},
8448905Srnordier	{'f', "..-."},
8548905Srnordier	{'g', "--."},
8648905Srnordier	{'h', "...."},
8748905Srnordier	{'i', ".."},
8848905Srnordier	{'j', ".---"},
8948905Srnordier	{'k', "-.-"},
9048905Srnordier	{'l', ".-.."},
9148905Srnordier	{'m', "--"},
9248905Srnordier	{'n', "-."},
9348905Srnordier	{'o', "---"},
9448905Srnordier	{'p', ".--."},
9548905Srnordier	{'q', "--.-"},
9648905Srnordier	{'r', ".-."},
9748905Srnordier	{'s', "..."},
9848905Srnordier	{'t', "-"},
9948905Srnordier	{'u', "..-"},
10048905Srnordier	{'v', "...-"},
10148905Srnordier	{'w', ".--"},
10248905Srnordier	{'x', "-..-"},
10348905Srnordier	{'y', "-.--"},
10448905Srnordier	{'z', "--.."},
10548905Srnordier
10648905Srnordier	/* digits */
10748905Srnordier
10848905Srnordier	{'0', "-----"},
10948905Srnordier	{'1', ".----"},
11048905Srnordier	{'2', "..---"},
11148905Srnordier	{'3', "...--"},
11248905Srnordier	{'4', "....-"},
11348905Srnordier	{'5', "....."},
11448905Srnordier	{'6', "-...."},
11548905Srnordier	{'7', "--..."},
11648905Srnordier	{'8', "---.."},
11748905Srnordier	{'9', "----."},
11848905Srnordier
11948905Srnordier	/* punctuation */
12048905Srnordier
12148905Srnordier	{',', "--..--"},
122	{'.', ".-.-.-"},
123	{'"', ".-..-."},
124	{'!', "..--."},
125	{'?', "..--.."},
126	{'/', "-..-."},
127	{'-', "-....-"},
128	{'=', "-...-"},		/* BT */
129	{':', "---..."},
130	{';', "-.-.-."},
131	{'(', "-.--."},		/* KN */
132	{')', "-.--.-"},
133	{'$', "...-..-"},
134	{'+', ".-.-."},		/* AR */
135	{'@', ".--.-."}
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