morse.c revision 78793
12490Sjkh/*
22490Sjkh * Copyright (c) 1988, 1993
32490Sjkh *	The Regents of the University of California.  All rights reserved.
42490Sjkh *
52490Sjkh * Redistribution and use in source and binary forms, with or without
62490Sjkh * modification, are permitted provided that the following conditions
72490Sjkh * are met:
82490Sjkh * 1. Redistributions of source code must retain the above copyright
92490Sjkh *    notice, this list of conditions and the following disclaimer.
102490Sjkh * 2. Redistributions in binary form must reproduce the above copyright
112490Sjkh *    notice, this list of conditions and the following disclaimer in the
122490Sjkh *    documentation and/or other materials provided with the distribution.
132490Sjkh * 3. All advertising materials mentioning features or use of this software
142490Sjkh *    must display the following acknowledgement:
152490Sjkh *	This product includes software developed by the University of
162490Sjkh *	California, Berkeley and its contributors.
172490Sjkh * 4. Neither the name of the University nor the names of its contributors
182490Sjkh *    may be used to endorse or promote products derived from this software
192490Sjkh *    without specific prior written permission.
202490Sjkh *
212490Sjkh * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
222490Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
232490Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
242490Sjkh * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
252490Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
262490Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
272490Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
282490Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
292490Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
302490Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
312490Sjkh * SUCH DAMAGE.
322490Sjkh */
332490Sjkh
3410352Sjoerg/*
3510352Sjoerg * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
3610352Sjoerg * <lyndon@orthanc.com>
3710352Sjoerg */
3810352Sjoerg
392490Sjkh#ifndef lint
4053920Sbillfstatic const char copyright[] =
412490Sjkh"@(#) Copyright (c) 1988, 1993\n\
422490Sjkh	The Regents of the University of California.  All rights reserved.\n";
432490Sjkh#endif /* not lint */
442490Sjkh
452490Sjkh#ifndef lint
4653920Sbillf#if 0
472490Sjkhstatic char sccsid[] = "@(#)morse.c	8.1 (Berkeley) 5/31/93";
4853920Sbillf#endif
4953920Sbillfstatic const char rcsid[] =
5053920Sbillf "$FreeBSD: head/games/morse/morse.c 78793 2001-06-26 01:43:52Z ache $";
512490Sjkh#endif /* not lint */
522490Sjkh
5357527Sjoerg#include <sys/time.h>
5457527Sjoerg
552490Sjkh#include <ctype.h>
5657527Sjoerg#include <fcntl.h>
5778793Sache#include <langinfo.h>
5810352Sjoerg#include <locale.h>
5957527Sjoerg#include <signal.h>
6057527Sjoerg#include <stdio.h>
6110352Sjoerg#include <stdlib.h>
6235857Sjb#include <string.h>
6357527Sjoerg#include <termios.h>
6454622Sbillf#include <unistd.h>
652490Sjkh
6610352Sjoerg#ifdef SPEAKER
6710352Sjoerg#include <machine/speaker.h>
6810352Sjoerg#endif
6910352Sjoerg
7010352Sjoergstruct morsetab {
7110352Sjoerg	char            inchar;
7210352Sjoerg	char           *morse;
732490Sjkh};
742490Sjkh
7554622Sbillfstatic const struct morsetab mtab[] = {
762490Sjkh
7710352Sjoerg	/* letters */
7810352Sjoerg
7954622Sbillf	{'a', ".-"},
8054622Sbillf	{'b', "-..."},
8154622Sbillf	{'c', "-.-."},
8254622Sbillf	{'d', "-.."},
8354622Sbillf	{'e', "."},
8454622Sbillf	{'f', "..-."},
8554622Sbillf	{'g', "--."},
8654622Sbillf	{'h', "...."},
8754622Sbillf	{'i', ".."},
8854622Sbillf	{'j', ".---"},
8954622Sbillf	{'k', "-.-"},
9054622Sbillf	{'l', ".-.."},
9154622Sbillf	{'m', "--"},
9254622Sbillf	{'n', "-."},
9354622Sbillf	{'o', "---"},
9454622Sbillf	{'p', ".--."},
9554622Sbillf	{'q', "--.-"},
9654622Sbillf	{'r', ".-."},
9754622Sbillf	{'s', "..."},
9854622Sbillf	{'t', "-"},
9954622Sbillf	{'u', "..-"},
10054622Sbillf	{'v', "...-"},
10154622Sbillf	{'w', ".--"},
10254622Sbillf	{'x', "-..-"},
10354622Sbillf	{'y', "-.--"},
10454622Sbillf	{'z', "--.."},
10510352Sjoerg
10610352Sjoerg	/* digits */
10710352Sjoerg
10854622Sbillf	{'0', "-----"},
10954622Sbillf	{'1', ".----"},
11054622Sbillf	{'2', "..---"},
11154622Sbillf	{'3', "...--"},
11254622Sbillf	{'4', "....-"},
11354622Sbillf	{'5', "....."},
11454622Sbillf	{'6', "-...."},
11554622Sbillf	{'7', "--..."},
11654622Sbillf	{'8', "---.."},
11754622Sbillf	{'9', "----."},
11810352Sjoerg
11910352Sjoerg	/* punctuation */
12010352Sjoerg
12154622Sbillf	{',', "--..--"},
12254622Sbillf	{'.', ".-.-.-"},
12354622Sbillf	{'?', "..--.."},
12454622Sbillf	{'/', "-..-."},
12554622Sbillf	{'-', "-....-"},
12654622Sbillf	{'=', "-...-"},		/* BT */
12754622Sbillf	{':', "---..."},
12854622Sbillf	{';', "-.-.-."},
12954622Sbillf	{'(', "-.--."},		/* KN */
13054622Sbillf	{')', "-.--.-"},
13154622Sbillf	{'$', "...-..-"},
13254622Sbillf	{'+', ".-.-."},		/* AR */
13310352Sjoerg
13410352Sjoerg	/* prosigns without already assigned values */
13510352Sjoerg
13654622Sbillf	{'#', ".-..."},		/* AS */
13754622Sbillf	{'@', "...-.-"},	/* SK */
13854622Sbillf	{'*', "...-."},		/* VE */
13954622Sbillf	{'%', "-...-.-"},	/* BK */
14010352Sjoerg
14154622Sbillf	{'\0', ""}
14210352Sjoerg};
14310352Sjoerg
14410352Sjoerg
14554622Sbillfstatic const struct morsetab iso8859tab[] = {
14654622Sbillf	{'�', ".--.-"},
14754622Sbillf	{'�', ".--.-"},
14854622Sbillf	{'�', ".--.-"},
14954622Sbillf	{'�', ".-.-"},
15054622Sbillf	{'�', "-.-.."},
15154622Sbillf	{'�', "..-.."},
15254622Sbillf	{'�', "..-.."},
15354622Sbillf	{'�', "-..-."},
15454622Sbillf	{'�', "---."},
15554622Sbillf	{'�', "..--"},
15610352Sjoerg
15754622Sbillf	{'\0', ""}
15810352Sjoerg};
15910352Sjoerg
16054622Sbillfstatic const struct morsetab koi8rtab[] = {
16110352Sjoerg	/*
16210352Sjoerg	 * the cyrillic alphabet; you'll need a KOI8R font in order
16310352Sjoerg	 * to see the actual characters
16410352Sjoerg	 */
16554622Sbillf	{'�', ".-"},		/* a */
16654622Sbillf	{'�', "-..."},	/* be */
16754622Sbillf	{'�', ".--"},	/* ve */
16854622Sbillf	{'�', "--."},	/* ge */
16954622Sbillf	{'�', "-.."},	/* de */
17054622Sbillf	{'�', "."},		/* ye */
17154622Sbillf	{'�', "."},         	/* yo, the same as ye */
17254622Sbillf	{'�', "...-"},	/* she */
17354622Sbillf	{'�', "--.."},	/* ze */
17454622Sbillf	{'�', ".."},		/* i */
17554622Sbillf	{'�', ".---"},	/* i kratkoye */
17654622Sbillf	{'�', "-.-"},	/* ka */
17754622Sbillf	{'�', ".-.."},	/* el */
17854622Sbillf	{'�', "--"},		/* em */
17954622Sbillf	{'�', "-."},		/* en */
18054622Sbillf	{'�', "---"},	/* o */
18154622Sbillf	{'�', ".--."},	/* pe */
18254622Sbillf	{'�', ".-."},	/* er */
18354622Sbillf	{'�', "..."},	/* es */
18454622Sbillf	{'�', "-"},		/* te */
18554622Sbillf	{'�', "..-"},	/* u */
18654622Sbillf	{'�', "..-."},	/* ef */
18754622Sbillf	{'�', "...."},	/* kha */
18854622Sbillf	{'�', "-.-."},	/* ce */
18954622Sbillf	{'�', "---."},	/* che */
19054622Sbillf	{'�', "----"},	/* sha */
19154622Sbillf	{'�', "--.-"},	/* shcha */
19254622Sbillf	{'�', "-.--"},	/* yi */
19354622Sbillf	{'�', "-..-"},	/* myakhkij znak */
19454622Sbillf	{'�', "..-.."},	/* ae */
19554622Sbillf	{'�', "..--"},	/* yu */
19654622Sbillf	{'�', ".-.-"},	/* ya */
19710352Sjoerg
19854622Sbillf	{'\0', ""}
19910352Sjoerg};
20010352Sjoerg
20110352Sjoergvoid            show(const char *), play(const char *), morse(char);
20257527Sjoergvoid		ttyout(const char *);
20357527Sjoergvoid		sighandler(int);
20410352Sjoerg
20557527Sjoerg#define GETOPTOPTS "d:ef:sw:"
20657527Sjoerg#define USAGE \
20757527Sjoerg"usage: morse [-s] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
20857527Sjoerg
20957527Sjoergstatic int      pflag, sflag, eflag;
21010352Sjoergstatic int      wpm = 20;	/* words per minute */
21110352Sjoerg#define FREQUENCY 600
21210352Sjoergstatic int      freq = FREQUENCY;
21357527Sjoergstatic char	*device;	/* for tty-controlled generator */
21410352Sjoerg
21510352Sjoerg#define DASH_LEN 3
21610352Sjoerg#define CHAR_SPACE 3
21710352Sjoerg#define WORD_SPACE (7 - CHAR_SPACE - 1)
21810352Sjoergstatic float    dot_clock;
21957527Sjoergint             spkr, line;
22057527Sjoergstruct termios	otty, ntty;
22157527Sjoergint		olflags;
22257527Sjoerg
22357527Sjoerg#ifdef SPEAKER
22410352Sjoergtone_t          sound;
22557527Sjoerg#undef GETOPTOPTS
22657527Sjoerg#define GETOPTOPTS "d:ef:psw:"
22757527Sjoerg#undef USAGE
22857527Sjoerg#define USAGE \
22957527Sjoerg"usage: morse [-s] [-p] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
23010352Sjoerg#endif
23110352Sjoerg
23278793Sachestatic const struct morsetab *hightab;
23310352Sjoerg
23410352Sjoergint
23510352Sjoergmain(int argc, char **argv)
2362490Sjkh{
23757527Sjoerg	int    ch, lflags;
23878793Sache	char  *p, *codeset;
2392490Sjkh
24057527Sjoerg	while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
24110352Sjoerg		switch ((char) ch) {
24257527Sjoerg		case 'd':
24357527Sjoerg			device = optarg;
24457527Sjoerg			break;
24557527Sjoerg		case 'e':
24657527Sjoerg			eflag = 1;
24757527Sjoerg			setvbuf(stdout, 0, _IONBF, 0);
24857527Sjoerg			break;
24910352Sjoerg		case 'f':
25010352Sjoerg			freq = atoi(optarg);
25110352Sjoerg			break;
25257527Sjoerg#ifdef SPEAKER
25310352Sjoerg		case 'p':
25410352Sjoerg			pflag = 1;
25510352Sjoerg			break;
25657527Sjoerg#endif
2572490Sjkh		case 's':
2582490Sjkh			sflag = 1;
2592490Sjkh			break;
26010352Sjoerg		case 'w':
26110352Sjoerg			wpm = atoi(optarg);
26210352Sjoerg			break;
2632490Sjkh		case '?':
2642490Sjkh		default:
26557527Sjoerg			fputs(USAGE, stderr);
2662490Sjkh			exit(1);
2672490Sjkh		}
26857527Sjoerg	if ((pflag || device) && sflag) {
26957527Sjoerg		fputs("morse: only one of -p, -d and -s allowed\n", stderr);
27010352Sjoerg		exit(1);
27110352Sjoerg	}
27257527Sjoerg	if ((pflag || device) && ((wpm < 1) || (wpm > 60))) {
27310352Sjoerg		fputs("morse: insane speed\n", stderr);
27410352Sjoerg		exit(1);
27510352Sjoerg	}
27657527Sjoerg	if ((pflag || device) && (freq == 0))
27710352Sjoerg		freq = FREQUENCY;
27810352Sjoerg
27910352Sjoerg#ifdef SPEAKER
28010352Sjoerg	if (pflag) {
28110352Sjoerg		if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
28210352Sjoerg			perror(SPEAKER);
28310352Sjoerg			exit(1);
28410352Sjoerg		}
28557527Sjoerg	} else
28657527Sjoerg#endif
28757527Sjoerg	if (device) {
28857527Sjoerg		if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
28957527Sjoerg			perror("open tty line");
29057527Sjoerg			exit(1);
29157527Sjoerg		}
29257527Sjoerg		if (tcgetattr(line, &otty) == -1) {
29357527Sjoerg			perror("tcgetattr() failed");
29457527Sjoerg			exit(1);
29557527Sjoerg		}
29657527Sjoerg		ntty = otty;
29757527Sjoerg		ntty.c_cflag |= CLOCAL;
29857527Sjoerg		tcsetattr(line, TCSANOW, &ntty);
29957527Sjoerg		lflags = fcntl(line, F_GETFL);
30057527Sjoerg		lflags &= ~O_NONBLOCK;
30157527Sjoerg		fcntl(line, F_SETFL, &lflags);
30257527Sjoerg		ioctl(line, TIOCMGET, &lflags);
30357527Sjoerg		lflags &= ~TIOCM_RTS;
30457527Sjoerg		olflags = lflags;
30557527Sjoerg		ioctl(line, TIOCMSET, &lflags);
30657527Sjoerg		(void)signal(SIGHUP, sighandler);
30757527Sjoerg		(void)signal(SIGINT, sighandler);
30857527Sjoerg		(void)signal(SIGQUIT, sighandler);
30957527Sjoerg		(void)signal(SIGTERM, sighandler);
31057527Sjoerg	}
31157527Sjoerg	if (pflag || device) {
31210352Sjoerg		dot_clock = wpm / 2.4;		/* dots/sec */
31310352Sjoerg		dot_clock = 1 / dot_clock;	/* duration of a dot */
31410352Sjoerg		dot_clock = dot_clock / 2;	/* dot_clock runs at twice */
31510352Sjoerg						/* the dot rate */
31610352Sjoerg		dot_clock = dot_clock * 100;	/* scale for ioctl */
31710352Sjoerg	}
31857527Sjoerg
3192490Sjkh	argc -= optind;
3202490Sjkh	argv += optind;
3212490Sjkh
32278793Sache	if (setlocale(LC_CTYPE, "") != NULL &&
32378793Sache	    *(codeset = nl_langinfo(CODESET)) != '\0') {
32478793Sache		if (strcmp(codeset, "KOI8-R") == 0)
32510352Sjoerg			hightab = koi8rtab;
32678793Sache		else if (strcmp(codeset, "ISO8859-1") == 0 ||
32778793Sache			 strcmp(codeset, "ISO8859-15") == 0)
32878793Sache			hightab = iso8859tab;
32910352Sjoerg	}
33010352Sjoerg
33110352Sjoerg	if (*argv) {
3322490Sjkh		do {
33310352Sjoerg			for (p = *argv; *p; ++p) {
33457527Sjoerg				if (eflag)
33557527Sjoerg					putchar(*p);
33629018Sache				morse(*p);
33710352Sjoerg			}
33857527Sjoerg			if (eflag)
33957527Sjoerg				putchar(' ');
34029018Sache			morse(' ');
3412490Sjkh		} while (*++argv);
34210352Sjoerg	} else {
34357527Sjoerg		while ((ch = getchar()) != EOF) {
34457527Sjoerg			if (eflag)
34557527Sjoerg				putchar(ch);
34610352Sjoerg			morse(ch);
34757527Sjoerg		}
34810352Sjoerg	}
34957527Sjoerg	if (device)
35057527Sjoerg		tcsetattr(line, TCSANOW, &otty);
35110352Sjoerg	exit(0);
3522490Sjkh}
3532490Sjkh
35410352Sjoergvoid
35510352Sjoergmorse(char c)
3562490Sjkh{
35754622Sbillf	const struct morsetab *m;
35810352Sjoerg
35929018Sache	if (isalpha((unsigned char)c))
36029018Sache		c = tolower((unsigned char)c);
36110352Sjoerg	if ((c == '\r') || (c == '\n'))
36210352Sjoerg		c = ' ';
36310352Sjoerg	if (c == ' ') {
36410352Sjoerg		if (pflag) {
36510352Sjoerg			play(" ");
36610352Sjoerg			return;
36757527Sjoerg		} else if (device) {
36857527Sjoerg			ttyout(" ");
36957527Sjoerg			return;
37010352Sjoerg		} else {
37110352Sjoerg			show("");
37210352Sjoerg			return;
37310352Sjoerg		}
37410352Sjoerg	}
37510352Sjoerg	for (m = ((unsigned char)c < 0x80? mtab: hightab);
37678793Sache	     m != NULL && m->inchar != '\0';
37710352Sjoerg	     m++) {
37810352Sjoerg		if (m->inchar == c) {
37910352Sjoerg			if (pflag) {
38010352Sjoerg				play(m->morse);
38157527Sjoerg			} else if (device) {
38257527Sjoerg				ttyout(m->morse);
38310352Sjoerg			} else
38410352Sjoerg				show(m->morse);
38510352Sjoerg		}
38610352Sjoerg	}
3872490Sjkh}
3882490Sjkh
38910352Sjoergvoid
39010352Sjoergshow(const char *s)
3912490Sjkh{
3922490Sjkh	if (sflag)
3932490Sjkh		printf(" %s", s);
39410352Sjoerg	else
39510352Sjoerg		for (; *s; ++s)
39610352Sjoerg			printf(" %s", *s == '.' ? "dit" : "dah");
39710352Sjoerg	printf("\n");
3982490Sjkh}
39910352Sjoerg
40010352Sjoergvoid
40110352Sjoergplay(const char *s)
40210352Sjoerg{
40310352Sjoerg#ifdef SPEAKER
40410352Sjoerg	const char *c;
40510352Sjoerg
40610352Sjoerg	for (c = s; *c != '\0'; c++) {
40729018Sache		switch (*c) {
40810352Sjoerg		case '.':
40910352Sjoerg			sound.frequency = freq;
41010352Sjoerg			sound.duration = dot_clock;
41110352Sjoerg			break;
41210352Sjoerg		case '-':
41310352Sjoerg			sound.frequency = freq;
41410352Sjoerg			sound.duration = dot_clock * DASH_LEN;
41510352Sjoerg			break;
41610352Sjoerg		case ' ':
41710352Sjoerg			sound.frequency = 0;
41810352Sjoerg			sound.duration = dot_clock * WORD_SPACE;
41910352Sjoerg			break;
42010352Sjoerg		default:
42110352Sjoerg			sound.duration = 0;
42210352Sjoerg		}
42310352Sjoerg		if (sound.duration) {
42410352Sjoerg			if (ioctl(spkr, SPKRTONE, &sound) == -1) {
42510352Sjoerg				perror("ioctl play");
42610352Sjoerg				exit(1);
42710352Sjoerg			}
42810352Sjoerg		}
42910352Sjoerg		sound.frequency = 0;
43010352Sjoerg		sound.duration = dot_clock;
43110352Sjoerg		if (ioctl(spkr, SPKRTONE, &sound) == -1) {
43210352Sjoerg			perror("ioctl rest");
43310352Sjoerg			exit(1);
43410352Sjoerg		}
43510352Sjoerg	}
43610352Sjoerg	sound.frequency = 0;
43710352Sjoerg	sound.duration = dot_clock * CHAR_SPACE;
43810352Sjoerg	ioctl(spkr, SPKRTONE, &sound);
43910352Sjoerg#endif
44010352Sjoerg}
44157527Sjoerg
44257527Sjoergvoid
44357527Sjoergttyout(const char *s)
44457527Sjoerg{
44557527Sjoerg	const char *c;
44657527Sjoerg	int duration, on, lflags;
44757527Sjoerg
44857527Sjoerg	for (c = s; *c != '\0'; c++) {
44957527Sjoerg		switch (*c) {
45057527Sjoerg		case '.':
45157527Sjoerg			on = 1;
45257527Sjoerg			duration = dot_clock;
45357527Sjoerg			break;
45457527Sjoerg		case '-':
45557527Sjoerg			on = 1;
45657527Sjoerg			duration = dot_clock * DASH_LEN;
45757527Sjoerg			break;
45857527Sjoerg		case ' ':
45957527Sjoerg			on = 0;
46057527Sjoerg			duration = dot_clock * WORD_SPACE;
46157527Sjoerg			break;
46257527Sjoerg		default:
46357527Sjoerg			on = 0;
46457527Sjoerg			duration = 0;
46557527Sjoerg		}
46657527Sjoerg		if (on) {
46757527Sjoerg			ioctl(line, TIOCMGET, &lflags);
46857527Sjoerg			lflags |= TIOCM_RTS;
46957527Sjoerg			ioctl(line, TIOCMSET, &lflags);
47057527Sjoerg		}
47157527Sjoerg		duration *= 10000;
47257527Sjoerg		if (duration)
47357527Sjoerg			usleep(duration);
47457527Sjoerg		ioctl(line, TIOCMGET, &lflags);
47557527Sjoerg		lflags &= ~TIOCM_RTS;
47657527Sjoerg		ioctl(line, TIOCMSET, &lflags);
47757527Sjoerg		duration = dot_clock * 10000;
47857527Sjoerg		usleep(duration);
47957527Sjoerg	}
48057527Sjoerg	duration = dot_clock * CHAR_SPACE * 10000;
48157527Sjoerg	usleep(duration);
48257527Sjoerg}
48357527Sjoerg
48457527Sjoergvoid
48557527Sjoergsighandler(int signo)
48657527Sjoerg{
48757527Sjoerg
48857527Sjoerg	ioctl(line, TIOCMSET, &olflags);
48957527Sjoerg	tcsetattr(line, TCSANOW, &otty);
49057527Sjoerg
49157527Sjoerg	signal(signo, SIG_DFL);
49257527Sjoerg	(void)kill(getpid(), signo);
49357527Sjoerg}
494