Merge from vendor branch SENDMAIL:
[dragonfly.git] / games / morse / morse.c
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 }