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