]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - games/morse/morse.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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
34 /*
35  * Taught to send *real* morse by Lyndon Nerenberg (VE6BBM)
36  * <lyndon@orthanc.ca>
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$";
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 <dev/speaker/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         {'/', "-..-."},
127         {'-', "-....-"},
128         {'=', "-...-"},         /* BT */
129         {':', "---..."},
130         {';', "-.-.-."},
131         {'(', "-.--."},         /* KN */
132         {')', "-.--.-"},
133         {'$', "...-..-"},
134         {'+', ".-.-."},         /* AR */
135         {'@', ".--.-."},        /* AC */
136
137         /* prosigns without already assigned values */
138
139         {'#', ".-..."},         /* AS */
140         {'&', "...-.-"},        /* SK */
141         {'*', "...-."},         /* VE */
142         {'%', "-...-.-"},       /* BK */
143
144         {'\0', ""}
145 };
146
147
148 static const struct morsetab iso8859_1tab[] = {
149         {'á', ".--.-"},
150         {'à', ".--.-"},
151         {'â', ".--.-"},
152         {'ä', ".-.-"},
153         {'ç', "-.-.."},
154         {'é', "..-.."},
155         {'è', "..-.."},
156         {'ê', "-..-."},
157         {'ö', "---."},
158         {'ü', "..--"},
159
160         {'\0', ""}
161 };
162
163 static const struct morsetab iso8859_7tab[] = {
164         /*
165          * The greek alphabet; you'll need an 8859-7 font in order
166          * to see the actual characters.
167          * This table does not implement:
168          * - the special sequences for the seven diphthongs,
169          * - the punctuation differences.
170          * Implementing these features would introduce too many
171          * special-cases in the program's main loop.
172          * The diphtong sequences are:
173          * alpha iota           .-.-
174          * alpha upsilon        ..--
175          * epsilon upsilon      ---.
176          * eta upsilon          ...-
177          * omikron iota         ---..
178          * omikron upsilon      ..-
179          * upsilon iota         .---
180          * The different punctuation symbols are:
181          * ;    ..-.-
182          * !    --..--
183          */
184         {'á', ".-"},    /* alpha */
185         {'Ü', ".-"},    /* alpha with acute */
186         {'â', "-..."},  /* beta */
187         {'ã', "--."},   /* gamma */
188         {'ä', "-.."},   /* delta */
189         {'å', "."},     /* epsilon */
190         {'Ý', "."},     /* epsilon with acute */
191         {'æ', "--.."},  /* zeta */
192         {'ç', "...."},  /* eta */
193         {'Þ', "...."},  /* eta with acute */
194         {'è', "-.-."},  /* theta */
195         {'é', ".."},    /* iota */
196         {'ß', ".."},    /* iota with acute */
197         {'ú', ".."},    /* iota with diairesis */
198         {'À', ".."},    /* iota with acute and diairesis */
199         {'ê', "-.-"},   /* kappa */
200         {'ë', ".-.."},  /* lamda */
201         {'ì', "--"},    /* mu */
202         {'í', "-."},    /* nu */
203         {'î', "-..-"},  /* xi */
204         {'ï', "---"},   /* omicron */
205         {'ü', "---"},   /* omicron with acute */
206         {'ð', ".--."},  /* pi */
207         {'ñ', ".-."},   /* rho */
208         {'ó', "..."},   /* sigma */
209         {'ò', "..."},   /* final sigma */
210         {'ô', "-"},     /* tau */
211         {'õ', "-.--"},  /* upsilon */
212         {'ý', "-.--"},  /* upsilon with acute */
213         {'û', "-.--"},  /* upsilon and diairesis */
214         {'à', "-.--"},  /* upsilon with acute and diairesis */
215         {'ö', "..-."},  /* phi */
216         {'÷', "----"},  /* chi */
217         {'ø', "--.-"},  /* psi */
218         {'ù', ".--"},   /* omega */
219         {'þ', ".--"},   /* omega with acute */
220
221         {'\0', ""}
222 };
223
224 static const struct morsetab koi8rtab[] = {
225         /*
226          * the cyrillic alphabet; you'll need a KOI8R font in order
227          * to see the actual characters
228          */
229         {'Á', ".-"},            /* a */
230         {'Â', "-..."},  /* be */
231         {'×', ".--"},   /* ve */
232         {'Ç', "--."},   /* ge */
233         {'Ä', "-.."},   /* de */
234         {'Å', "."},             /* ye */
235         {'£', "."},             /* yo, the same as ye */
236         {'Ö', "...-"},  /* she */
237         {'Ú', "--.."},  /* ze */
238         {'É', ".."},            /* i */
239         {'Ê', ".---"},  /* i kratkoye */
240         {'Ë', "-.-"},   /* ka */
241         {'Ì', ".-.."},  /* el */
242         {'Í', "--"},            /* em */
243         {'Î', "-."},            /* en */
244         {'Ï', "---"},   /* o */
245         {'Ð', ".--."},  /* pe */
246         {'Ò', ".-."},   /* er */
247         {'Ó', "..."},   /* es */
248         {'Ô', "-"},             /* te */
249         {'Õ', "..-"},   /* u */
250         {'Æ', "..-."},  /* ef */
251         {'È', "...."},  /* kha */
252         {'Ã', "-.-."},  /* ce */
253         {'Þ', "---."},  /* che */
254         {'Û', "----"},  /* sha */
255         {'Ý', "--.-"},  /* shcha */
256         {'Ù', "-.--"},  /* yi */
257         {'Ø', "-..-"},  /* myakhkij znak */
258         {'Ü', "..-.."}, /* ae */
259         {'À', "..--"},  /* yu */
260         {'Ñ', ".-.-"},  /* ya */
261
262         {'\0', ""}
263 };
264
265 void            show(const char *), play(const char *), morse(char);
266 void            ttyout(const char *);
267 void            sighandler(int);
268
269 #define GETOPTOPTS "c:d:ef:lsw:"
270 #define USAGE \
271 "usage: morse [-els] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
272
273 static int      pflag, lflag, sflag, eflag;
274 static int      wpm = 20;       /* effective words per minute */
275 static int      cpm;            /* effective words per minute between
276                                  * characters */
277 #define FREQUENCY 600
278 static int      freq = FREQUENCY;
279 static char     *device;        /* for tty-controlled generator */
280
281 #define DASH_LEN 3
282 #define CHAR_SPACE 3
283 #define WORD_SPACE (7 - CHAR_SPACE - 1)
284 static float    dot_clock;
285 static float    cdot_clock;
286 int             spkr, line;
287 struct termios  otty, ntty;
288 int             olflags;
289
290 #ifdef SPEAKER
291 tone_t          sound;
292 #undef GETOPTOPTS
293 #define GETOPTOPTS "c:d:ef:lpsw:"
294 #undef USAGE
295 #define USAGE \
296 "usage: morse [-elps] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
297 #endif
298
299 static const struct morsetab *hightab;
300
301 int
302 main(int argc, char **argv)
303 {
304         int    ch, lflags;
305         char  *p, *codeset;
306
307         while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
308                 switch ((char) ch) {
309                 case 'c':
310                         cpm = atoi(optarg);
311                         break;
312                 case 'd':
313                         device = optarg;
314                         break;
315                 case 'e':
316                         eflag = 1;
317                         setvbuf(stdout, 0, _IONBF, 0);
318                         break;
319                 case 'f':
320                         freq = atoi(optarg);
321                         break;
322                 case 'l':
323                         lflag = 1;
324                         break;
325 #ifdef SPEAKER
326                 case 'p':
327                         pflag = 1;
328                         break;
329 #endif
330                 case 's':
331                         sflag = 1;
332                         break;
333                 case 'w':
334                         wpm = atoi(optarg);
335                         break;
336                 case '?':
337                 default:
338                         fputs(USAGE, stderr);
339                         exit(1);
340                 }
341         if (sflag && lflag) {
342                 fputs("morse: only one of -l and -s allowed\n", stderr);
343                 exit(1);
344         }
345         if ((pflag || device) && (sflag || lflag)) {
346                 fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr);
347                 exit(1);
348         }
349         if (cpm == 0)
350                 cpm = wpm;
351         if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) {
352                 fputs("morse: insane speed\n", stderr);
353                 exit(1);
354         }
355         if ((pflag || device) && (freq == 0))
356                 freq = FREQUENCY;
357
358 #ifdef SPEAKER
359         if (pflag) {
360                 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
361                         perror(SPEAKER);
362                         exit(1);
363                 }
364         } else
365 #endif
366         if (device) {
367                 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
368                         perror("open tty line");
369                         exit(1);
370                 }
371                 if (tcgetattr(line, &otty) == -1) {
372                         perror("tcgetattr() failed");
373                         exit(1);
374                 }
375                 ntty = otty;
376                 ntty.c_cflag |= CLOCAL;
377                 tcsetattr(line, TCSANOW, &ntty);
378                 lflags = fcntl(line, F_GETFL);
379                 lflags &= ~O_NONBLOCK;
380                 fcntl(line, F_SETFL, &lflags);
381                 ioctl(line, TIOCMGET, &lflags);
382                 lflags &= ~TIOCM_RTS;
383                 olflags = lflags;
384                 ioctl(line, TIOCMSET, &lflags);
385                 (void)signal(SIGHUP, sighandler);
386                 (void)signal(SIGINT, sighandler);
387                 (void)signal(SIGQUIT, sighandler);
388                 (void)signal(SIGTERM, sighandler);
389         }
390         if (pflag || device) {
391                 dot_clock = wpm / 2.4;          /* dots/sec */
392                 dot_clock = 1 / dot_clock;      /* duration of a dot */
393                 dot_clock = dot_clock / 2;      /* dot_clock runs at twice */
394                                                 /* the dot rate */
395                 dot_clock = dot_clock * 100;    /* scale for ioctl */
396
397                 cdot_clock = cpm / 2.4;         /* dots/sec */
398                 cdot_clock = 1 / cdot_clock;    /* duration of a dot */
399                 cdot_clock = cdot_clock / 2;    /* dot_clock runs at twice */
400                                                 /* the dot rate */
401                 cdot_clock = cdot_clock * 100;  /* scale for ioctl */
402         }
403
404         argc -= optind;
405         argv += optind;
406
407         if (setlocale(LC_CTYPE, "") != NULL &&
408             *(codeset = nl_langinfo(CODESET)) != '\0') {
409                 if (strcmp(codeset, "KOI8-R") == 0)
410                         hightab = koi8rtab;
411                 else if (strcmp(codeset, "ISO8859-1") == 0 ||
412                          strcmp(codeset, "ISO8859-15") == 0)
413                         hightab = iso8859_1tab;
414                 else if (strcmp(codeset, "ISO8859-7") == 0)
415                         hightab = iso8859_7tab;
416         }
417
418         if (lflag)
419                 printf("m");
420         if (*argv) {
421                 do {
422                         for (p = *argv; *p; ++p) {
423                                 if (eflag)
424                                         putchar(*p);
425                                 morse(*p);
426                         }
427                         if (eflag)
428                                 putchar(' ');
429                         morse(' ');
430                 } while (*++argv);
431         } else {
432                 while ((ch = getchar()) != EOF) {
433                         if (eflag)
434                                 putchar(ch);
435                         morse(ch);
436                 }
437         }
438         if (device)
439                 tcsetattr(line, TCSANOW, &otty);
440         exit(0);
441 }
442
443 void
444 morse(char c)
445 {
446         const struct morsetab *m;
447
448         if (isalpha((unsigned char)c))
449                 c = tolower((unsigned char)c);
450         if ((c == '\r') || (c == '\n'))
451                 c = ' ';
452         if (c == ' ') {
453                 if (pflag)
454                         play(" ");
455                 else if (device)
456                         ttyout(" ");
457                 else if (lflag)
458                         printf("\n");
459                 else
460                         show("");
461                 return;
462         }
463         for (m = ((unsigned char)c < 0x80? mtab: hightab);
464              m != NULL && m->inchar != '\0';
465              m++) {
466                 if (m->inchar == c) {
467                         if (pflag) {
468                                 play(m->morse);
469                         } else if (device) {
470                                 ttyout(m->morse);
471                         } else
472                                 show(m->morse);
473                 }
474         }
475 }
476
477 void
478 show(const char *s)
479 {
480         if (lflag) {
481                 printf("%s ", s);
482         } else if (sflag) {
483                 printf(" %s\n", s);
484         } else {
485                 for (; *s; ++s)
486                         printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" :
487                             "di" : "dah");
488                 printf("\n");
489         }
490 }
491
492 void
493 play(const char *s)
494 {
495 #ifdef SPEAKER
496         const char *c;
497
498         for (c = s; *c != '\0'; c++) {
499                 switch (*c) {
500                 case '.':
501                         sound.frequency = freq;
502                         sound.duration = dot_clock;
503                         break;
504                 case '-':
505                         sound.frequency = freq;
506                         sound.duration = dot_clock * DASH_LEN;
507                         break;
508                 case ' ':
509                         sound.frequency = 0;
510                         sound.duration = cdot_clock * WORD_SPACE;
511                         break;
512                 default:
513                         sound.duration = 0;
514                 }
515                 if (sound.duration) {
516                         if (ioctl(spkr, SPKRTONE, &sound) == -1) {
517                                 perror("ioctl play");
518                                 exit(1);
519                         }
520                 }
521                 sound.frequency = 0;
522                 sound.duration = dot_clock;
523                 if (ioctl(spkr, SPKRTONE, &sound) == -1) {
524                         perror("ioctl rest");
525                         exit(1);
526                 }
527         }
528         sound.frequency = 0;
529         sound.duration = cdot_clock * CHAR_SPACE;
530         ioctl(spkr, SPKRTONE, &sound);
531 #endif
532 }
533
534 void
535 ttyout(const char *s)
536 {
537         const char *c;
538         int duration, on, lflags;
539
540         for (c = s; *c != '\0'; c++) {
541                 switch (*c) {
542                 case '.':
543                         on = 1;
544                         duration = dot_clock;
545                         break;
546                 case '-':
547                         on = 1;
548                         duration = dot_clock * DASH_LEN;
549                         break;
550                 case ' ':
551                         on = 0;
552                         duration = cdot_clock * WORD_SPACE;
553                         break;
554                 default:
555                         on = 0;
556                         duration = 0;
557                 }
558                 if (on) {
559                         ioctl(line, TIOCMGET, &lflags);
560                         lflags |= TIOCM_RTS;
561                         ioctl(line, TIOCMSET, &lflags);
562                 }
563                 duration *= 10000;
564                 if (duration)
565                         usleep(duration);
566                 ioctl(line, TIOCMGET, &lflags);
567                 lflags &= ~TIOCM_RTS;
568                 ioctl(line, TIOCMSET, &lflags);
569                 duration = dot_clock * 10000;
570                 usleep(duration);
571         }
572         duration = cdot_clock * CHAR_SPACE * 10000;
573         usleep(duration);
574 }
575
576 void
577 sighandler(int signo)
578 {
579
580         ioctl(line, TIOCMSET, &olflags);
581         tcsetattr(line, TCSANOW, &otty);
582
583         signal(signo, SIG_DFL);
584         (void)kill(getpid(), signo);
585 }