xref: /dragonfly/games/morse/morse.c (revision 5de36205)
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  * @(#) Copyright (c) 1988, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)morse.c	8.1 (Berkeley) 5/31/93
35  * $FreeBSD: src/games/morse/morse.c,v 1.12.2.2 2002/03/12 17:45:15 phantom Exp $
36  * $DragonFly: src/games/morse/morse.c,v 1.3 2005/04/25 16:10:24 liamfoy Exp $
37  */
38 
39 /*
40  * Taught to send *real* morse by Lyndon Nerenberg (VE7TCP/VE6BBM)
41  * <lyndon@orthanc.com>
42  */
43 
44 #include <sys/time.h>
45 
46 #include <ctype.h>
47 #include <fcntl.h>
48 #include <langinfo.h>
49 #include <locale.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <termios.h>
55 #include <unistd.h>
56 
57 #ifdef SPEAKER
58 #include <machine/speaker.h>
59 #endif
60 
61 struct morsetab {
62 	char            inchar;
63 	const char      *morse;
64 };
65 
66 static const struct morsetab mtab[] = {
67 
68 	/* letters */
69 
70 	{'a', ".-"},
71 	{'b', "-..."},
72 	{'c', "-.-."},
73 	{'d', "-.."},
74 	{'e', "."},
75 	{'f', "..-."},
76 	{'g', "--."},
77 	{'h', "...."},
78 	{'i', ".."},
79 	{'j', ".---"},
80 	{'k', "-.-"},
81 	{'l', ".-.."},
82 	{'m', "--"},
83 	{'n', "-."},
84 	{'o', "---"},
85 	{'p', ".--."},
86 	{'q', "--.-"},
87 	{'r', ".-."},
88 	{'s', "..."},
89 	{'t', "-"},
90 	{'u', "..-"},
91 	{'v', "...-"},
92 	{'w', ".--"},
93 	{'x', "-..-"},
94 	{'y', "-.--"},
95 	{'z', "--.."},
96 
97 	/* digits */
98 
99 	{'0', "-----"},
100 	{'1', ".----"},
101 	{'2', "..---"},
102 	{'3', "...--"},
103 	{'4', "....-"},
104 	{'5', "....."},
105 	{'6', "-...."},
106 	{'7', "--..."},
107 	{'8', "---.."},
108 	{'9', "----."},
109 
110 	/* punctuation */
111 
112 	{',', "--..--"},
113 	{'.', ".-.-.-"},
114 	{'?', "..--.."},
115 	{'/', "-..-."},
116 	{'-', "-....-"},
117 	{'=', "-...-"},		/* BT */
118 	{':', "---..."},
119 	{';', "-.-.-."},
120 	{'(', "-.--."},		/* KN */
121 	{')', "-.--.-"},
122 	{'$', "...-..-"},
123 	{'+', ".-.-."},		/* AR */
124 
125 	/* prosigns without already assigned values */
126 
127 	{'#', ".-..."},		/* AS */
128 	{'@', "...-.-"},	/* SK */
129 	{'*', "...-."},		/* VE */
130 	{'%', "-...-.-"},	/* BK */
131 
132 	{'\0', ""}
133 };
134 
135 
136 static const struct morsetab iso8859tab[] = {
137 	{'�', ".--.-"},
138 	{'�', ".--.-"},
139 	{'�', ".--.-"},
140 	{'�', ".-.-"},
141 	{'�', "-.-.."},
142 	{'�', "..-.."},
143 	{'�', "..-.."},
144 	{'�', "-..-."},
145 	{'�', "---."},
146 	{'�', "..--"},
147 
148 	{'\0', ""}
149 };
150 
151 static const struct morsetab koi8rtab[] = {
152 	/*
153 	 * the cyrillic alphabet; you'll need a KOI8R font in order
154 	 * to see the actual characters
155 	 */
156 	{'�', ".-"},		/* a */
157 	{'�', "-..."},	/* be */
158 	{'�', ".--"},	/* ve */
159 	{'�', "--."},	/* ge */
160 	{'�', "-.."},	/* de */
161 	{'�', "."},		/* ye */
162 	{'�', "."},         	/* yo, the same as ye */
163 	{'�', "...-"},	/* she */
164 	{'�', "--.."},	/* ze */
165 	{'�', ".."},		/* i */
166 	{'�', ".---"},	/* i kratkoye */
167 	{'�', "-.-"},	/* ka */
168 	{'�', ".-.."},	/* el */
169 	{'�', "--"},		/* em */
170 	{'�', "-."},		/* en */
171 	{'�', "---"},	/* o */
172 	{'�', ".--."},	/* pe */
173 	{'�', ".-."},	/* er */
174 	{'�', "..."},	/* es */
175 	{'�', "-"},		/* te */
176 	{'�', "..-"},	/* u */
177 	{'�', "..-."},	/* ef */
178 	{'�', "...."},	/* kha */
179 	{'�', "-.-."},	/* ce */
180 	{'�', "---."},	/* che */
181 	{'�', "----"},	/* sha */
182 	{'�', "--.-"},	/* shcha */
183 	{'�', "-.--"},	/* yi */
184 	{'�', "-..-"},	/* myakhkij znak */
185 	{'�', "..-.."},	/* ae */
186 	{'�', "..--"},	/* yu */
187 	{'�', ".-.-"},	/* ya */
188 
189 	{'\0', ""}
190 };
191 
192 void            show(const char *), play(const char *), morse(char);
193 void		ttyout(const char *);
194 void		sighandler(int);
195 
196 #define GETOPTOPTS "d:ef:sw:"
197 #define USAGE \
198 "usage: morse [-s] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
199 
200 static int      pflag, sflag, eflag;
201 static int      wpm = 20;	/* words per minute */
202 #define FREQUENCY 600
203 static int      freq = FREQUENCY;
204 static char	*device;	/* for tty-controlled generator */
205 
206 #define DASH_LEN 3
207 #define CHAR_SPACE 3
208 #define WORD_SPACE (7 - CHAR_SPACE - 1)
209 static float    dot_clock;
210 int             spkr, line;
211 struct termios	otty, ntty;
212 int		olflags;
213 
214 #ifdef SPEAKER
215 tone_t          sound;
216 #undef GETOPTOPTS
217 #define GETOPTOPTS "d:ef:psw:"
218 #undef USAGE
219 #define USAGE \
220 "usage: morse [-s] [-p] [-e] [-d device] [-w speed] [-f frequency] [string ...]\n"
221 #endif
222 
223 static const struct morsetab *hightab;
224 
225 int
226 main(int argc, char **argv)
227 {
228 	int    ch, lflags;
229 	char  *p, *codeset;
230 
231 	while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
232 		switch ((char) ch) {
233 		case 'd':
234 			device = optarg;
235 			break;
236 		case 'e':
237 			eflag = 1;
238 			setvbuf(stdout, 0, _IONBF, 0);
239 			break;
240 		case 'f':
241 			freq = atoi(optarg);
242 			break;
243 #ifdef SPEAKER
244 		case 'p':
245 			pflag = 1;
246 			break;
247 #endif
248 		case 's':
249 			sflag = 1;
250 			break;
251 		case 'w':
252 			wpm = atoi(optarg);
253 			break;
254 		case '?':
255 		default:
256 			fputs(USAGE, stderr);
257 			exit(1);
258 		}
259 	if ((pflag || device) && sflag) {
260 		fputs("morse: only one of -p, -d and -s allowed\n", stderr);
261 		exit(1);
262 	}
263 	if ((pflag || device) && ((wpm < 1) || (wpm > 60))) {
264 		fputs("morse: insane speed\n", stderr);
265 		exit(1);
266 	}
267 	if ((pflag || device) && (freq == 0))
268 		freq = FREQUENCY;
269 
270 #ifdef SPEAKER
271 	if (pflag) {
272 		if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
273 			perror(SPEAKER);
274 			exit(1);
275 		}
276 	} else
277 #endif
278 	if (device) {
279 		if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
280 			perror("open tty line");
281 			exit(1);
282 		}
283 		if (tcgetattr(line, &otty) == -1) {
284 			perror("tcgetattr() failed");
285 			exit(1);
286 		}
287 		ntty = otty;
288 		ntty.c_cflag |= CLOCAL;
289 		tcsetattr(line, TCSANOW, &ntty);
290 		lflags = fcntl(line, F_GETFL);
291 		lflags &= ~O_NONBLOCK;
292 		fcntl(line, F_SETFL, &lflags);
293 		ioctl(line, TIOCMGET, &lflags);
294 		lflags &= ~TIOCM_RTS;
295 		olflags = lflags;
296 		ioctl(line, TIOCMSET, &lflags);
297 		(void)signal(SIGHUP, sighandler);
298 		(void)signal(SIGINT, sighandler);
299 		(void)signal(SIGQUIT, sighandler);
300 		(void)signal(SIGTERM, sighandler);
301 	}
302 	if (pflag || device) {
303 		dot_clock = wpm / 2.4;		/* dots/sec */
304 		dot_clock = 1 / dot_clock;	/* duration of a dot */
305 		dot_clock = dot_clock / 2;	/* dot_clock runs at twice */
306 						/* the dot rate */
307 		dot_clock = dot_clock * 100;	/* scale for ioctl */
308 	}
309 
310 	argc -= optind;
311 	argv += optind;
312 
313 	if (setlocale(LC_CTYPE, "") != NULL &&
314 	    *(codeset = nl_langinfo(CODESET)) != '\0') {
315 		if (strcmp(codeset, "KOI8-R") == 0)
316 			hightab = koi8rtab;
317 		else if (strcmp(codeset, "ISO8859-1") == 0 ||
318 			 strcmp(codeset, "ISO8859-15") == 0)
319 			hightab = iso8859tab;
320 	}
321 
322 	if (*argv) {
323 		do {
324 			for (p = *argv; *p; ++p) {
325 				if (eflag)
326 					putchar(*p);
327 				morse(*p);
328 			}
329 			if (eflag)
330 				putchar(' ');
331 			morse(' ');
332 		} while (*++argv);
333 	} else {
334 		while ((ch = getchar()) != EOF) {
335 			if (eflag)
336 				putchar(ch);
337 			morse(ch);
338 		}
339 	}
340 	if (device)
341 		tcsetattr(line, TCSANOW, &otty);
342 	exit(0);
343 }
344 
345 void
346 morse(char c)
347 {
348 	const struct morsetab *m;
349 
350 	if (isalpha((unsigned char)c))
351 		c = tolower((unsigned char)c);
352 	if ((c == '\r') || (c == '\n'))
353 		c = ' ';
354 	if (c == ' ') {
355 		if (pflag) {
356 			play(" ");
357 			return;
358 		} else if (device) {
359 			ttyout(" ");
360 			return;
361 		} else {
362 			show("");
363 			return;
364 		}
365 	}
366 	for (m = ((unsigned char)c < 0x80? mtab: hightab);
367 	     m != NULL && m->inchar != '\0';
368 	     m++) {
369 		if (m->inchar == c) {
370 			if (pflag) {
371 				play(m->morse);
372 			} else if (device) {
373 				ttyout(m->morse);
374 			} else
375 				show(m->morse);
376 		}
377 	}
378 }
379 
380 void
381 show(const char *s)
382 {
383 	if (sflag)
384 		printf(" %s", s);
385 	else
386 		for (; *s; ++s)
387 			printf(" %s", *s == '.' ? "dit" : "dah");
388 	printf("\n");
389 }
390 
391 void
392 play(const char *s)
393 {
394 #ifdef SPEAKER
395 	const char *c;
396 
397 	for (c = s; *c != '\0'; c++) {
398 		switch (*c) {
399 		case '.':
400 			sound.frequency = freq;
401 			sound.duration = dot_clock;
402 			break;
403 		case '-':
404 			sound.frequency = freq;
405 			sound.duration = dot_clock * DASH_LEN;
406 			break;
407 		case ' ':
408 			sound.frequency = 0;
409 			sound.duration = dot_clock * WORD_SPACE;
410 			break;
411 		default:
412 			sound.duration = 0;
413 		}
414 		if (sound.duration) {
415 			if (ioctl(spkr, SPKRTONE, &sound) == -1) {
416 				perror("ioctl play");
417 				exit(1);
418 			}
419 		}
420 		sound.frequency = 0;
421 		sound.duration = dot_clock;
422 		if (ioctl(spkr, SPKRTONE, &sound) == -1) {
423 			perror("ioctl rest");
424 			exit(1);
425 		}
426 	}
427 	sound.frequency = 0;
428 	sound.duration = dot_clock * CHAR_SPACE;
429 	ioctl(spkr, SPKRTONE, &sound);
430 #endif
431 }
432 
433 void
434 ttyout(const char *s)
435 {
436 	const char *c;
437 	int duration, on, lflags;
438 
439 	for (c = s; *c != '\0'; c++) {
440 		switch (*c) {
441 		case '.':
442 			on = 1;
443 			duration = dot_clock;
444 			break;
445 		case '-':
446 			on = 1;
447 			duration = dot_clock * DASH_LEN;
448 			break;
449 		case ' ':
450 			on = 0;
451 			duration = dot_clock * WORD_SPACE;
452 			break;
453 		default:
454 			on = 0;
455 			duration = 0;
456 		}
457 		if (on) {
458 			ioctl(line, TIOCMGET, &lflags);
459 			lflags |= TIOCM_RTS;
460 			ioctl(line, TIOCMSET, &lflags);
461 		}
462 		duration *= 10000;
463 		if (duration)
464 			usleep(duration);
465 		ioctl(line, TIOCMGET, &lflags);
466 		lflags &= ~TIOCM_RTS;
467 		ioctl(line, TIOCMSET, &lflags);
468 		duration = dot_clock * 10000;
469 		usleep(duration);
470 	}
471 	duration = dot_clock * CHAR_SPACE * 10000;
472 	usleep(duration);
473 }
474 
475 void
476 sighandler(int signo)
477 {
478 
479 	ioctl(line, TIOCMSET, &olflags);
480 	tcsetattr(line, TCSANOW, &otty);
481 
482 	signal(signo, SIG_DFL);
483 	(void)kill(getpid(), signo);
484 }
485