]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/morse/morse.c
ping: use the monotonic clock to measure durations
[FreeBSD/FreeBSD.git] / usr.bin / 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 static const char copyright[] =
36 "@(#) Copyright (c) 1988, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38
39 #if 0
40 static char sccsid[] = "@(#)morse.c     8.1 (Berkeley) 5/31/93";
41 #endif
42 static const char rcsid[] =
43  "$FreeBSD$";
44
45 #include <sys/time.h>
46 #include <sys/ioctl.h>
47
48 #include <ctype.h>
49 #include <err.h>
50 #include <fcntl.h>
51 #include <langinfo.h>
52 #include <locale.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <termios.h>
58 #include <unistd.h>
59
60 #ifdef __FreeBSD__
61 /* Always use the speaker, let the open fail if -p is selected */
62 #define SPEAKER "/dev/speaker"
63 #endif
64
65 #define WHITESPACE " \t\n"
66 #define DELIMITERS " \t"
67
68 #ifdef SPEAKER
69 #include <dev/speaker/speaker.h>
70 #endif
71
72 struct morsetab {
73         const char      inchar;
74         const char     *morse;
75 };
76
77 static const struct morsetab mtab[] = {
78
79         /* letters */
80
81         {'a', ".-"},
82         {'b', "-..."},
83         {'c', "-.-."},
84         {'d', "-.."},
85         {'e', "."},
86         {'f', "..-."},
87         {'g', "--."},
88         {'h', "...."},
89         {'i', ".."},
90         {'j', ".---"},
91         {'k', "-.-"},
92         {'l', ".-.."},
93         {'m', "--"},
94         {'n', "-."},
95         {'o', "---"},
96         {'p', ".--."},
97         {'q', "--.-"},
98         {'r', ".-."},
99         {'s', "..."},
100         {'t', "-"},
101         {'u', "..-"},
102         {'v', "...-"},
103         {'w', ".--"},
104         {'x', "-..-"},
105         {'y', "-.--"},
106         {'z', "--.."},
107
108         /* digits */
109
110         {'0', "-----"},
111         {'1', ".----"},
112         {'2', "..---"},
113         {'3', "...--"},
114         {'4', "....-"},
115         {'5', "....."},
116         {'6', "-...."},
117         {'7', "--..."},
118         {'8', "---.."},
119         {'9', "----."},
120
121         /* punctuation */
122
123         {',', "--..--"},
124         {'.', ".-.-.-"},
125         {'"', ".-..-."},
126         {'!', "..--."},
127         {'?', "..--.."},
128         {'/', "-..-."},
129         {'-', "-....-"},
130         {'=', "-...-"},         /* BT */
131         {':', "---..."},
132         {';', "-.-.-."},
133         {'(', "-.--."},         /* KN */
134         {')', "-.--.-"},
135         {'$', "...-..-"},
136         {'+', ".-.-."},         /* AR */
137         {'@', ".--.-."},        /* AC */
138         {'_', "..--.-"},
139         {'\'', ".----."},
140
141         /* prosigns without already assigned values */
142
143         {'#', ".-..."},         /* AS */
144         {'&', "...-.-"},        /* SK */
145         {'*', "...-."},         /* VE */
146         {'%', "-...-.-"},       /* BK */
147
148         {'\0', ""}
149 };
150
151 /*
152  * Code-points for some Latin1 chars in ISO-8859-1 encoding.
153  * UTF-8 encoded chars in the comments.
154  */
155 static const struct morsetab iso8859_1tab[] = {
156         {'\340', ".--.-"},      /* à */
157         {'\341', ".--.-"},      /* á */
158         {'\342', ".--.-"},      /* â */
159         {'\344', ".-.-"},       /* ä */
160         {'\347', "-.-.."},      /* ç */
161         {'\350', "..-.."},      /* è */
162         {'\351', "..-.."},      /* é */
163         {'\352', "-..-."},      /* ê */
164         {'\361', "--.--"},      /* ñ */
165         {'\366', "---."},       /* ö */
166         {'\374', "..--"},       /* ü */
167
168         {'\0', ""}
169 };
170
171 /*
172  * Code-points for some Greek chars in ISO-8859-7 encoding.
173  * UTF-8 encoded chars in the comments.
174  */
175 static const struct morsetab iso8859_7tab[] = {
176         /*
177          * This table does not implement:
178          * - the special sequences for the seven diphthongs,
179          * - the punctuation differences.
180          * Implementing these features would introduce too many
181          * special-cases in the program's main loop.
182          * The diphthong sequences are:
183          * alpha iota           .-.-
184          * alpha upsilon        ..--
185          * epsilon upsilon      ---.
186          * eta upsilon          ...-
187          * omicron iota         ---..
188          * omicron upsilon      ..-
189          * upsilon iota         .---
190          * The different punctuation symbols are:
191          * ;    ..-.-
192          * !    --..--
193          */
194         {'\341', ".-"},         /* α, alpha */
195         {'\334', ".-"},         /* ά, alpha with acute */
196         {'\342', "-..."},       /* β, beta */
197         {'\343', "--."},        /* γ, gamma */
198         {'\344', "-.."},        /* δ, delta */
199         {'\345', "."},          /* ε, epsilon */
200         {'\335', "."},          /* έ, epsilon with acute */
201         {'\346', "--.."},       /* ζ, zeta */
202         {'\347', "...."},       /* η, eta */
203         {'\336', "...."},       /* ή, eta with acute */
204         {'\350', "-.-."},       /* θ, theta */
205         {'\351', ".."},         /* ι, iota */
206         {'\337', ".."},         /* ί, iota with acute */
207         {'\372', ".."},         /* ϊ, iota with diaeresis */
208         {'\300', ".."},         /* ΐ, iota with acute and diaeresis */
209         {'\352', "-.-"},        /* κ, kappa */
210         {'\353', ".-.."},       /* λ, lambda */
211         {'\354', "--"},         /* μ, mu */
212         {'\355', "-."},         /* ν, nu */
213         {'\356', "-..-"},       /* ξ, xi */
214         {'\357', "---"},        /* ο, omicron */
215         {'\374', "---"},        /* ό, omicron with acute */
216         {'\360', ".--."},       /* π, pi */
217         {'\361', ".-."},        /* ρ, rho */
218         {'\363', "..."},        /* σ, sigma */
219         {'\362', "..."},        /* ς, final sigma */
220         {'\364', "-"},          /* τ, tau */
221         {'\365', "-.--"},       /* υ, upsilon */
222         {'\375', "-.--"},       /* ύ, upsilon with acute */
223         {'\373', "-.--"},       /* ϋ, upsilon and diaeresis */
224         {'\340', "-.--"},       /* ΰ, upsilon with acute and diaeresis */
225         {'\366', "..-."},       /* φ, phi */
226         {'\367', "----"},       /* χ, chi */
227         {'\370', "--.-"},       /* ψ, psi */
228         {'\371', ".--"},        /* ω, omega */
229         {'\376', ".--"},        /* ώ, omega with acute */
230
231         {'\0', ""}
232 };
233
234 /*
235  * Code-points for the Cyrillic alphabet in KOI8-R encoding.
236  * UTF-8 encoded chars in the comments.
237  */
238 static const struct morsetab koi8rtab[] = {
239         {'\301', ".-"},         /* а, a */
240         {'\302', "-..."},       /* б, be */
241         {'\327', ".--"},        /* в, ve */
242         {'\307', "--."},        /* г, ge */
243         {'\304', "-.."},        /* д, de */
244         {'\305', "."},          /* е, ye */
245         {'\243', "."},          /* ё, yo, the same as ye */
246         {'\326', "...-"},       /* ж, she */
247         {'\332', "--.."},       /* з, ze */
248         {'\311', ".."},         /* и, i */
249         {'\312', ".---"},       /* й, i kratkoye */
250         {'\313', "-.-"},        /* к, ka */
251         {'\314', ".-.."},       /* л, el */
252         {'\315', "--"},         /* м, em */
253         {'\316', "-."},         /* н, en */
254         {'\317', "---"},        /* о, o */
255         {'\320', ".--."},       /* п, pe */
256         {'\322', ".-."},        /* р, er */
257         {'\323', "..."},        /* с, es */
258         {'\324', "-"},          /* т, te */
259         {'\325', "..-"},        /* у, u */
260         {'\306', "..-."},       /* ф, ef */
261         {'\310', "...."},       /* х, kha */
262         {'\303', "-.-."},       /* ц, ce */
263         {'\336', "---."},       /* ч, che */
264         {'\333', "----"},       /* ш, sha */
265         {'\335', "--.-"},       /* щ, shcha */
266         {'\331', "-.--"},       /* ы, yi */
267         {'\330', "-..-"},       /* ь, myakhkij znak */
268         {'\334', "..-.."},      /* э, ae */
269         {'\300', "..--"},       /* ю, yu */
270         {'\321', ".-.-"},       /* я, ya */
271
272         {'\0', ""}
273 };
274
275 static void     show(const char *), play(const char *), morse(char);
276 static void     decode (char *), fdecode(FILE *);
277 static void     ttyout(const char *);
278 static void     sighandler(int);
279
280 static int      pflag, lflag, rflag, sflag, eflag;
281 static int      wpm = 20;       /* effective words per minute */
282 static int      cpm;            /* effective words per minute between
283                                  * characters */
284 #define FREQUENCY 600
285 static int      freq = FREQUENCY;
286 static char     *device;        /* for tty-controlled generator */
287
288 #define DASH_LEN 3
289 #define CHAR_SPACE 3
290 #define WORD_SPACE (7 - CHAR_SPACE - 1)
291 static float    dot_clock;
292 static float    cdot_clock;
293 static int      spkr, line;
294 static struct termios otty, ntty;
295 static int      olflags;
296
297 #ifdef SPEAKER
298 static tone_t   sound;
299 #define GETOPTOPTS "c:d:ef:lprsw:"
300 #define USAGE \
301 "usage: morse [-elprs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
302 #else
303 #define GETOPTOPTS "c:d:ef:lrsw:"
304 #define USAGE \
305 "usage: morse [-elrs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
306
307 #endif
308
309 static const struct morsetab *hightab;
310
311 int
312 main(int argc, char *argv[])
313 {
314         int    ch, lflags;
315         char  *p, *codeset;
316
317         while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
318                 switch ((char) ch) {
319                 case 'c':
320                         cpm = atoi(optarg);
321                         break;
322                 case 'd':
323                         device = optarg;
324                         break;
325                 case 'e':
326                         eflag = 1;
327                         setvbuf(stdout, 0, _IONBF, 0);
328                         break;
329                 case 'f':
330                         freq = atoi(optarg);
331                         break;
332                 case 'l':
333                         lflag = 1;
334                         break;
335 #ifdef SPEAKER
336                 case 'p':
337                         pflag = 1;
338                         break;
339 #endif
340                 case 'r':
341                         rflag = 1;
342                         break;
343                 case 's':
344                         sflag = 1;
345                         break;
346                 case 'w':
347                         wpm = atoi(optarg);
348                         break;
349                 case '?':
350                 default:
351                         errx(1, USAGE);
352                 }
353         if ((sflag && lflag) || (sflag && rflag) || (lflag && rflag)) {
354                 errx(1, "morse: only one of -l, -s, and -r allowed\n");
355         }
356         if ((pflag || device) && (sflag || lflag)) {
357                 errx(1, "morse: only one of -p, -d and -l, -s allowed\n");
358         }
359         if (cpm == 0) {
360                 cpm = wpm;
361         }
362         if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) {
363                 errx(1, "morse: insane speed\n");
364         }
365         if ((pflag || device) && (freq == 0)) {
366                 freq = FREQUENCY;
367         }
368 #ifdef SPEAKER
369         if (pflag) {
370                 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
371                         err(1, SPEAKER);
372                 }
373         } else
374 #endif
375         if (device) {
376                 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
377                         err(1, "open tty line");
378                 }
379                 if (tcgetattr(line, &otty) == -1) {
380                         err(1, "tcgetattr() failed");
381                 }
382                 ntty = otty;
383                 ntty.c_cflag |= CLOCAL;
384                 tcsetattr(line, TCSANOW, &ntty);
385                 lflags = fcntl(line, F_GETFL);
386                 lflags &= ~O_NONBLOCK;
387                 fcntl(line, F_SETFL, &lflags);
388                 ioctl(line, TIOCMGET, &lflags);
389                 lflags &= ~TIOCM_RTS;
390                 olflags = lflags;
391                 ioctl(line, TIOCMSET, &lflags);
392                 (void)signal(SIGHUP, sighandler);
393                 (void)signal(SIGINT, sighandler);
394                 (void)signal(SIGQUIT, sighandler);
395                 (void)signal(SIGTERM, sighandler);
396         }
397         if (pflag || device) {
398                 dot_clock = wpm / 2.4;          /* dots/sec */
399                 dot_clock = 1 / dot_clock;      /* duration of a dot */
400                 dot_clock = dot_clock / 2;      /* dot_clock runs at twice */
401                                                 /* the dot rate */
402                 dot_clock = dot_clock * 100;    /* scale for ioctl */
403
404                 cdot_clock = cpm / 2.4;         /* dots/sec */
405                 cdot_clock = 1 / cdot_clock;    /* duration of a dot */
406                 cdot_clock = cdot_clock / 2;    /* dot_clock runs at twice */
407                                                 /* the dot rate */
408                 cdot_clock = cdot_clock * 100;  /* scale for ioctl */
409         }
410
411         argc -= optind;
412         argv += optind;
413
414         if (setlocale(LC_CTYPE, "") != NULL &&
415             *(codeset = nl_langinfo(CODESET)) != '\0') {
416                 if (strcmp(codeset, "KOI8-R") == 0)
417                         hightab = koi8rtab;
418                 else if (strcmp(codeset, "ISO8859-1") == 0 ||
419                          strcmp(codeset, "ISO8859-15") == 0)
420                         hightab = iso8859_1tab;
421                 else if (strcmp(codeset, "ISO8859-7") == 0)
422                         hightab = iso8859_7tab;
423         }
424
425         if (lflag) {
426                 printf("m");
427         }
428         if (rflag) {
429                 if (*argv) {
430                         do {
431                                 p = strtok(*argv, DELIMITERS);
432                                 if (p == NULL) {
433                                         decode(*argv);
434                                 }
435                                 else {
436                                         while (p) {
437                                                 decode(p);
438                                                 p = strtok(NULL, DELIMITERS);
439                                         }
440                                 }
441                         } while (*++argv);
442                         putchar('\n');
443                 } else {
444                         fdecode(stdin);
445                 }
446         }
447         else if (*argv) {
448                 do {
449                         for (p = *argv; *p; ++p) {
450                                 if (eflag)
451                                         putchar(*p);
452                                 morse(*p);
453                         }
454                         if (eflag)
455                                 putchar(' ');
456                         morse(' ');
457                 } while (*++argv);
458         } else {
459                 while ((ch = getchar()) != EOF) {
460                         if (eflag)
461                                 putchar(ch);
462                         morse(ch);
463                 }
464         }
465         if (device)
466                 tcsetattr(line, TCSANOW, &otty);
467         exit(0);
468 }
469
470 static void
471 morse(char c)
472 {
473         const struct morsetab *m;
474
475         if (isalpha((unsigned char)c))
476                 c = tolower((unsigned char)c);
477         if ((c == '\r') || (c == '\n'))
478                 c = ' ';
479         if (c == ' ') {
480                 if (pflag)
481                         play(" ");
482                 else if (device)
483                         ttyout(" ");
484                 else if (lflag)
485                         printf("\n");
486                 else
487                         show("");
488                 return;
489         }
490         for (m = ((unsigned char)c < 0x80? mtab: hightab);
491              m != NULL && m->inchar != '\0';
492              m++) {
493                 if (m->inchar == c) {
494                         if (pflag) {
495                                 play(m->morse);
496                         } else if (device) {
497                                 ttyout(m->morse);
498                         } else
499                                 show(m->morse);
500                 }
501         }
502 }
503
504 static void
505 show(const char *s)
506 {
507         if (lflag) {
508                 printf("%s ", s);
509         } else if (sflag) {
510                 printf(" %s\n", s);
511         } else {
512                 for (; *s; ++s)
513                         printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" :
514                             "di" : "dah");
515                 printf("\n");
516         }
517 }
518
519 static void
520 play(const char *s)
521 {
522 #ifdef SPEAKER
523         const char *c;
524
525         for (c = s; *c != '\0'; c++) {
526                 switch (*c) {
527                 case '.':
528                         sound.frequency = freq;
529                         sound.duration = dot_clock;
530                         break;
531                 case '-':
532                         sound.frequency = freq;
533                         sound.duration = dot_clock * DASH_LEN;
534                         break;
535                 case ' ':
536                         sound.frequency = 0;
537                         sound.duration = cdot_clock * WORD_SPACE;
538                         break;
539                 default:
540                         sound.duration = 0;
541                 }
542                 if (sound.duration) {
543                         if (ioctl(spkr, SPKRTONE, &sound) == -1) {
544                                 err(1, "ioctl play");
545                         }
546                 }
547                 sound.frequency = 0;
548                 sound.duration = dot_clock;
549                 if (ioctl(spkr, SPKRTONE, &sound) == -1) {
550                         err(1, "ioctl rest");
551                 }
552         }
553         sound.frequency = 0;
554         sound.duration = cdot_clock * CHAR_SPACE;
555         ioctl(spkr, SPKRTONE, &sound);
556 #endif
557 }
558
559 static void
560 ttyout(const char *s)
561 {
562         const char *c;
563         int duration, on, lflags;
564
565         for (c = s; *c != '\0'; c++) {
566                 switch (*c) {
567                 case '.':
568                         on = 1;
569                         duration = dot_clock;
570                         break;
571                 case '-':
572                         on = 1;
573                         duration = dot_clock * DASH_LEN;
574                         break;
575                 case ' ':
576                         on = 0;
577                         duration = cdot_clock * WORD_SPACE;
578                         break;
579                 default:
580                         on = 0;
581                         duration = 0;
582                 }
583                 if (on) {
584                         ioctl(line, TIOCMGET, &lflags);
585                         lflags |= TIOCM_RTS;
586                         ioctl(line, TIOCMSET, &lflags);
587                 }
588                 duration *= 10000;
589                 if (duration)
590                         usleep(duration);
591                 ioctl(line, TIOCMGET, &lflags);
592                 lflags &= ~TIOCM_RTS;
593                 ioctl(line, TIOCMSET, &lflags);
594                 duration = dot_clock * 10000;
595                 usleep(duration);
596         }
597         duration = cdot_clock * CHAR_SPACE * 10000;
598         usleep(duration);
599 }
600
601 void
602 fdecode(FILE *stream)
603 {
604         char *n, *p, *s;
605         char buf[BUFSIZ];
606
607         s = buf;
608         while (fgets(s, BUFSIZ - (s - buf), stream)) {
609                 p = buf;
610
611                 while (*p && isblank(*p)) {
612                         p++;
613                 }
614                 while (*p && isspace(*p)) {
615                         p++;
616                         putchar (' ');
617                 }
618                 while (*p) {
619                         n = strpbrk(p, WHITESPACE);
620                         if (n == NULL) {
621                                 /* The token was interrupted at the end
622                                  * of the buffer. Shift it to the begin
623                                  * of the buffer.
624                                  */
625                                 for (s = buf; *p; *s++ = *p++)
626                                         ;
627                         } else {
628                                 *n = '\0';
629                                 n++;
630                                 decode(p);
631                                 p = n;
632                         }
633                 }
634         }
635         putchar('\n');
636 }
637
638 void
639 decode(char *p)
640 {
641         char c;
642         const struct morsetab *m;
643
644         c = ' ';
645         for (m = mtab; m != NULL && m->inchar != '\0'; m++) {
646                 if (strcmp(m->morse, p) == 0) {
647                         c = m->inchar;
648                         break;
649                 }
650         }
651
652         if (c == ' ')
653                 for (m = hightab; m != NULL && m->inchar != '\0'; m++) {
654                         if (strcmp(m->morse, p) == 0) {
655                                 c = m->inchar;
656                                 break;
657                         }
658                 }
659
660         putchar(c);
661 }
662
663 static void
664 sighandler(int signo)
665 {
666
667         ioctl(line, TIOCMSET, &olflags);
668         tcsetattr(line, TCSANOW, &otty);
669
670         signal(signo, SIG_DFL);
671         (void)kill(getpid(), signo);
672 }