]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - games/morse/morse.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.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. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 /*
31  * Taught to send *real* morse by Lyndon Nerenberg (VE6BBM)
32  * <lyndon@orthanc.ca>
33  */
34
35 #ifndef lint
36 static const char copyright[] =
37 "@(#) Copyright (c) 1988, 1993\n\
38         The Regents of the University of California.  All rights reserved.\n";
39 #endif /* not lint */
40
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)morse.c     8.1 (Berkeley) 5/31/93";
44 #endif
45 static const char rcsid[] =
46  "$FreeBSD$";
47 #endif /* not lint */
48
49 #include <sys/time.h>
50
51 #include <ctype.h>
52 #include <fcntl.h>
53 #include <langinfo.h>
54 #include <locale.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <termios.h>
60 #include <unistd.h>
61
62 /* Always use the speaker, let the open fail if -p is selected */
63 #define SPEAKER "/dev/speaker"
64
65 #ifdef SPEAKER
66 #include <dev/speaker/speaker.h>
67 #endif
68
69 struct morsetab {
70         const char      inchar;
71         const char     *morse;
72 };
73
74 static const struct morsetab mtab[] = {
75
76         /* letters */
77
78         {'a', ".-"},
79         {'b', "-..."},
80         {'c', "-.-."},
81         {'d', "-.."},
82         {'e', "."},
83         {'f', "..-."},
84         {'g', "--."},
85         {'h', "...."},
86         {'i', ".."},
87         {'j', ".---"},
88         {'k', "-.-"},
89         {'l', ".-.."},
90         {'m', "--"},
91         {'n', "-."},
92         {'o', "---"},
93         {'p', ".--."},
94         {'q', "--.-"},
95         {'r', ".-."},
96         {'s', "..."},
97         {'t', "-"},
98         {'u', "..-"},
99         {'v', "...-"},
100         {'w', ".--"},
101         {'x', "-..-"},
102         {'y', "-.--"},
103         {'z', "--.."},
104
105         /* digits */
106
107         {'0', "-----"},
108         {'1', ".----"},
109         {'2', "..---"},
110         {'3', "...--"},
111         {'4', "....-"},
112         {'5', "....."},
113         {'6', "-...."},
114         {'7', "--..."},
115         {'8', "---.."},
116         {'9', "----."},
117
118         /* punctuation */
119
120         {',', "--..--"},
121         {'.', ".-.-.-"},
122         {'"', ".-..-."},
123         {'!', "..--."},
124         {'?', "..--.."},
125         {'/', "-..-."},
126         {'-', "-....-"},
127         {'=', "-...-"},         /* BT */
128         {':', "---..."},
129         {';', "-.-.-."},
130         {'(', "-.--."},         /* KN */
131         {')', "-.--.-"},
132         {'$', "...-..-"},
133         {'+', ".-.-."},         /* AR */
134         {'@', ".--.-."},        /* AC */
135
136         /* prosigns without already assigned values */
137
138         {'#', ".-..."},         /* AS */
139         {'&', "...-.-"},        /* SK */
140         {'*', "...-."},         /* VE */
141         {'%', "-...-.-"},       /* BK */
142
143         {'\0', ""}
144 };
145
146
147 static const struct morsetab iso8859_1tab[] = {
148         {'á', ".--.-"},
149         {'à', ".--.-"},
150         {'â', ".--.-"},
151         {'ä', ".-.-"},
152         {'ç', "-.-.."},
153         {'é', "..-.."},
154         {'è', "..-.."},
155         {'ê', "-..-."},
156         {'ö', "---."},
157         {'ü', "..--"},
158
159         {'\0', ""}
160 };
161
162 static const struct morsetab iso8859_7tab[] = {
163         /*
164          * The Greek alphabet; you'll need an ISO8859-7 font in order
165          * to see the actual characters.
166          * This table does not implement:
167          * - the special sequences for the seven diphthongs,
168          * - the punctuation differences.
169          * Implementing these features would introduce too many
170          * special-cases in the program's main loop.
171          * The diphthong sequences are:
172          * alpha iota           .-.-
173          * alpha upsilon        ..--
174          * epsilon upsilon      ---.
175          * eta upsilon          ...-
176          * omicron iota         ---..
177          * omicron upsilon      ..-
178          * upsilon iota         .---
179          * The different punctuation symbols are:
180          * ;    ..-.-
181          * !    --..--
182          */
183         {'á', ".-"},    /* alpha */
184         {'Ü', ".-"},    /* alpha with acute */
185         {'â', "-..."},  /* beta */
186         {'ã', "--."},   /* gamma */
187         {'ä', "-.."},   /* delta */
188         {'å', "."},     /* epsilon */
189         {'Ý', "."},     /* epsilon with acute */
190         {'æ', "--.."},  /* zeta */
191         {'ç', "...."},  /* eta */
192         {'Þ', "...."},  /* eta with acute */
193         {'è', "-.-."},  /* theta */
194         {'é', ".."},    /* iota */
195         {'ß', ".."},    /* iota with acute */
196         {'ú', ".."},    /* iota with diaeresis */
197         {'À', ".."},    /* iota with acute and diaeresis */
198         {'ê', "-.-"},   /* kappa */
199         {'ë', ".-.."},  /* lambda */
200         {'ì', "--"},    /* mu */
201         {'í', "-."},    /* nu */
202         {'î', "-..-"},  /* xi */
203         {'ï', "---"},   /* omicron */
204         {'ü', "---"},   /* omicron with acute */
205         {'ð', ".--."},  /* pi */
206         {'ñ', ".-."},   /* rho */
207         {'ó', "..."},   /* sigma */
208         {'ò', "..."},   /* final sigma */
209         {'ô', "-"},     /* tau */
210         {'õ', "-.--"},  /* upsilon */
211         {'ý', "-.--"},  /* upsilon with acute */
212         {'û', "-.--"},  /* upsilon and diaeresis */
213         {'à', "-.--"},  /* upsilon with acute and diaeresis */
214         {'ö', "..-."},  /* phi */
215         {'÷', "----"},  /* chi */
216         {'ø', "--.-"},  /* psi */
217         {'ù', ".--"},   /* omega */
218         {'þ', ".--"},   /* omega with acute */
219
220         {'\0', ""}
221 };
222
223 static const struct morsetab koi8rtab[] = {
224         /*
225          * The Cyrillic alphabet; you'll need a KOI8-R font in order
226          * to see the actual characters
227          */
228         {'Á', ".-"},    /* a */
229         {'Â', "-..."},  /* be */
230         {'×', ".--"},   /* ve */
231         {'Ç', "--."},   /* ge */
232         {'Ä', "-.."},   /* de */
233         {'Å', "."},     /* ye */
234         {'£', "."},     /* yo, the same as ye */
235         {'Ö', "...-"},  /* she */
236         {'Ú', "--.."},  /* ze */
237         {'É', ".."},    /* i */
238         {'Ê', ".---"},  /* i kratkoye */
239         {'Ë', "-.-"},   /* ka */
240         {'Ì', ".-.."},  /* el */
241         {'Í', "--"},    /* em */
242         {'Î', "-."},    /* en */
243         {'Ï', "---"},   /* o */
244         {'Ð', ".--."},  /* pe */
245         {'Ò', ".-."},   /* er */
246         {'Ó', "..."},   /* es */
247         {'Ô', "-"},     /* te */
248         {'Õ', "..-"},   /* u */
249         {'Æ', "..-."},  /* ef */
250         {'È', "...."},  /* kha */
251         {'Ã', "-.-."},  /* ce */
252         {'Þ', "---."},  /* che */
253         {'Û', "----"},  /* sha */
254         {'Ý', "--.-"},  /* shcha */
255         {'Ù', "-.--"},  /* yi */
256         {'Ø', "-..-"},  /* myakhkij znak */
257         {'Ü', "..-.."}, /* ae */
258         {'À', "..--"},  /* yu */
259         {'Ñ', ".-.-"},  /* ya */
260
261         {'\0', ""}
262 };
263
264 void            show(const char *), play(const char *), morse(char);
265 void            ttyout(const char *);
266 void            sighandler(int);
267
268 #define GETOPTOPTS "c:d:ef:lsw:"
269 #define USAGE \
270 "usage: morse [-els] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
271
272 static int      pflag, lflag, sflag, eflag;
273 static int      wpm = 20;       /* effective words per minute */
274 static int      cpm;            /* effective words per minute between
275                                  * characters */
276 #define FREQUENCY 600
277 static int      freq = FREQUENCY;
278 static char     *device;        /* for tty-controlled generator */
279
280 #define DASH_LEN 3
281 #define CHAR_SPACE 3
282 #define WORD_SPACE (7 - CHAR_SPACE - 1)
283 static float    dot_clock;
284 static float    cdot_clock;
285 int             spkr, line;
286 struct termios  otty, ntty;
287 int             olflags;
288
289 #ifdef SPEAKER
290 tone_t          sound;
291 #undef GETOPTOPTS
292 #define GETOPTOPTS "c:d:ef:lpsw:"
293 #undef USAGE
294 #define USAGE \
295 "usage: morse [-elps] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
296 #endif
297
298 static const struct morsetab *hightab;
299
300 int
301 main(int argc, char **argv)
302 {
303         int    ch, lflags;
304         char  *p, *codeset;
305
306         while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
307                 switch ((char) ch) {
308                 case 'c':
309                         cpm = atoi(optarg);
310                         break;
311                 case 'd':
312                         device = optarg;
313                         break;
314                 case 'e':
315                         eflag = 1;
316                         setvbuf(stdout, 0, _IONBF, 0);
317                         break;
318                 case 'f':
319                         freq = atoi(optarg);
320                         break;
321                 case 'l':
322                         lflag = 1;
323                         break;
324 #ifdef SPEAKER
325                 case 'p':
326                         pflag = 1;
327                         break;
328 #endif
329                 case 's':
330                         sflag = 1;
331                         break;
332                 case 'w':
333                         wpm = atoi(optarg);
334                         break;
335                 case '?':
336                 default:
337                         fputs(USAGE, stderr);
338                         exit(1);
339                 }
340         if (sflag && lflag) {
341                 fputs("morse: only one of -l and -s allowed\n", stderr);
342                 exit(1);
343         }
344         if ((pflag || device) && (sflag || lflag)) {
345                 fputs("morse: only one of -p, -d and -l, -s allowed\n", stderr);
346                 exit(1);
347         }
348         if (cpm == 0)
349                 cpm = wpm;
350         if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) {
351                 fputs("morse: insane speed\n", stderr);
352                 exit(1);
353         }
354         if ((pflag || device) && (freq == 0))
355                 freq = FREQUENCY;
356
357 #ifdef SPEAKER
358         if (pflag) {
359                 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
360                         perror(SPEAKER);
361                         exit(1);
362                 }
363         } else
364 #endif
365         if (device) {
366                 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
367                         perror("open tty line");
368                         exit(1);
369                 }
370                 if (tcgetattr(line, &otty) == -1) {
371                         perror("tcgetattr() failed");
372                         exit(1);
373                 }
374                 ntty = otty;
375                 ntty.c_cflag |= CLOCAL;
376                 tcsetattr(line, TCSANOW, &ntty);
377                 lflags = fcntl(line, F_GETFL);
378                 lflags &= ~O_NONBLOCK;
379                 fcntl(line, F_SETFL, &lflags);
380                 ioctl(line, TIOCMGET, &lflags);
381                 lflags &= ~TIOCM_RTS;
382                 olflags = lflags;
383                 ioctl(line, TIOCMSET, &lflags);
384                 (void)signal(SIGHUP, sighandler);
385                 (void)signal(SIGINT, sighandler);
386                 (void)signal(SIGQUIT, sighandler);
387                 (void)signal(SIGTERM, sighandler);
388         }
389         if (pflag || device) {
390                 dot_clock = wpm / 2.4;          /* dots/sec */
391                 dot_clock = 1 / dot_clock;      /* duration of a dot */
392                 dot_clock = dot_clock / 2;      /* dot_clock runs at twice */
393                                                 /* the dot rate */
394                 dot_clock = dot_clock * 100;    /* scale for ioctl */
395
396                 cdot_clock = cpm / 2.4;         /* dots/sec */
397                 cdot_clock = 1 / cdot_clock;    /* duration of a dot */
398                 cdot_clock = cdot_clock / 2;    /* dot_clock runs at twice */
399                                                 /* the dot rate */
400                 cdot_clock = cdot_clock * 100;  /* scale for ioctl */
401         }
402
403         argc -= optind;
404         argv += optind;
405
406         if (setlocale(LC_CTYPE, "") != NULL &&
407             *(codeset = nl_langinfo(CODESET)) != '\0') {
408                 if (strcmp(codeset, "KOI8-R") == 0)
409                         hightab = koi8rtab;
410                 else if (strcmp(codeset, "ISO8859-1") == 0 ||
411                          strcmp(codeset, "ISO8859-15") == 0)
412                         hightab = iso8859_1tab;
413                 else if (strcmp(codeset, "ISO8859-7") == 0)
414                         hightab = iso8859_7tab;
415         }
416
417         if (lflag)
418                 printf("m");
419         if (*argv) {
420                 do {
421                         for (p = *argv; *p; ++p) {
422                                 if (eflag)
423                                         putchar(*p);
424                                 morse(*p);
425                         }
426                         if (eflag)
427                                 putchar(' ');
428                         morse(' ');
429                 } while (*++argv);
430         } else {
431                 while ((ch = getchar()) != EOF) {
432                         if (eflag)
433                                 putchar(ch);
434                         morse(ch);
435                 }
436         }
437         if (device)
438                 tcsetattr(line, TCSANOW, &otty);
439         exit(0);
440 }
441
442 void
443 morse(char c)
444 {
445         const struct morsetab *m;
446
447         if (isalpha((unsigned char)c))
448                 c = tolower((unsigned char)c);
449         if ((c == '\r') || (c == '\n'))
450                 c = ' ';
451         if (c == ' ') {
452                 if (pflag)
453                         play(" ");
454                 else if (device)
455                         ttyout(" ");
456                 else if (lflag)
457                         printf("\n");
458                 else
459                         show("");
460                 return;
461         }
462         for (m = ((unsigned char)c < 0x80? mtab: hightab);
463              m != NULL && m->inchar != '\0';
464              m++) {
465                 if (m->inchar == c) {
466                         if (pflag) {
467                                 play(m->morse);
468                         } else if (device) {
469                                 ttyout(m->morse);
470                         } else
471                                 show(m->morse);
472                 }
473         }
474 }
475
476 void
477 show(const char *s)
478 {
479         if (lflag) {
480                 printf("%s ", s);
481         } else if (sflag) {
482                 printf(" %s\n", s);
483         } else {
484                 for (; *s; ++s)
485                         printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" :
486                             "di" : "dah");
487                 printf("\n");
488         }
489 }
490
491 void
492 play(const char *s)
493 {
494 #ifdef SPEAKER
495         const char *c;
496
497         for (c = s; *c != '\0'; c++) {
498                 switch (*c) {
499                 case '.':
500                         sound.frequency = freq;
501                         sound.duration = dot_clock;
502                         break;
503                 case '-':
504                         sound.frequency = freq;
505                         sound.duration = dot_clock * DASH_LEN;
506                         break;
507                 case ' ':
508                         sound.frequency = 0;
509                         sound.duration = cdot_clock * WORD_SPACE;
510                         break;
511                 default:
512                         sound.duration = 0;
513                 }
514                 if (sound.duration) {
515                         if (ioctl(spkr, SPKRTONE, &sound) == -1) {
516                                 perror("ioctl play");
517                                 exit(1);
518                         }
519                 }
520                 sound.frequency = 0;
521                 sound.duration = dot_clock;
522                 if (ioctl(spkr, SPKRTONE, &sound) == -1) {
523                         perror("ioctl rest");
524                         exit(1);
525                 }
526         }
527         sound.frequency = 0;
528         sound.duration = cdot_clock * CHAR_SPACE;
529         ioctl(spkr, SPKRTONE, &sound);
530 #endif
531 }
532
533 void
534 ttyout(const char *s)
535 {
536         const char *c;
537         int duration, on, lflags;
538
539         for (c = s; *c != '\0'; c++) {
540                 switch (*c) {
541                 case '.':
542                         on = 1;
543                         duration = dot_clock;
544                         break;
545                 case '-':
546                         on = 1;
547                         duration = dot_clock * DASH_LEN;
548                         break;
549                 case ' ':
550                         on = 0;
551                         duration = cdot_clock * WORD_SPACE;
552                         break;
553                 default:
554                         on = 0;
555                         duration = 0;
556                 }
557                 if (on) {
558                         ioctl(line, TIOCMGET, &lflags);
559                         lflags |= TIOCM_RTS;
560                         ioctl(line, TIOCMSET, &lflags);
561                 }
562                 duration *= 10000;
563                 if (duration)
564                         usleep(duration);
565                 ioctl(line, TIOCMGET, &lflags);
566                 lflags &= ~TIOCM_RTS;
567                 ioctl(line, TIOCMSET, &lflags);
568                 duration = dot_clock * 10000;
569                 usleep(duration);
570         }
571         duration = cdot_clock * CHAR_SPACE * 10000;
572         usleep(duration);
573 }
574
575 void
576 sighandler(int signo)
577 {
578
579         ioctl(line, TIOCMSET, &olflags);
580         tcsetattr(line, TCSANOW, &otty);
581
582         signal(signo, SIG_DFL);
583         (void)kill(getpid(), signo);
584 }