2 * Copyright (c) 1988, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
31 * Taught to send *real* morse by Lyndon Nerenberg (VE6BBM)
35 static const char copyright[] =
36 "@(#) Copyright (c) 1988, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
40 static char sccsid[] = "@(#)morse.c 8.1 (Berkeley) 5/31/93";
44 #include <sys/ioctl.h>
59 /* Always use the speaker, let the open fail if -p is selected */
60 #define SPEAKER "/dev/speaker"
63 #define WHITESPACE " \t\n"
64 #define DELIMITERS " \t"
67 #include <dev/speaker/speaker.h>
75 static const struct morsetab mtab[] = {
128 {'=', "-...-"}, /* BT */
131 {'(', "-.--."}, /* KN */
134 {'+', ".-.-."}, /* AR */
135 {'@', ".--.-."}, /* AC */
139 /* prosigns without already assigned values */
141 {'#', ".-..."}, /* AS */
142 {'&', "...-.-"}, /* SK */
143 {'*', "...-."}, /* VE */
144 {'%', "-...-.-"}, /* BK */
150 * Code-points for some Latin1 chars in ISO-8859-1 encoding.
151 * UTF-8 encoded chars in the comments.
153 static const struct morsetab iso8859_1tab[] = {
154 {'\340', ".--.-"}, /* à */
155 {'\341', ".--.-"}, /* á */
156 {'\342', ".--.-"}, /* â */
157 {'\344', ".-.-"}, /* ä */
158 {'\347', "-.-.."}, /* ç */
159 {'\350', "..-.."}, /* è */
160 {'\351', "..-.."}, /* é */
161 {'\352', "-..-."}, /* ê */
162 {'\361', "--.--"}, /* ñ */
163 {'\366', "---."}, /* ö */
164 {'\374', "..--"}, /* ü */
170 * Code-points for some Greek chars in ISO-8859-7 encoding.
171 * UTF-8 encoded chars in the comments.
173 static const struct morsetab iso8859_7tab[] = {
175 * This table does not implement:
176 * - the special sequences for the seven diphthongs,
177 * - the punctuation differences.
178 * Implementing these features would introduce too many
179 * special-cases in the program's main loop.
180 * The diphthong sequences are:
183 * epsilon upsilon ---.
186 * omicron upsilon ..-
188 * The different punctuation symbols are:
192 {'\341', ".-"}, /* α, alpha */
193 {'\334', ".-"}, /* ά, alpha with acute */
194 {'\342', "-..."}, /* β, beta */
195 {'\343', "--."}, /* γ, gamma */
196 {'\344', "-.."}, /* δ, delta */
197 {'\345', "."}, /* ε, epsilon */
198 {'\335', "."}, /* έ, epsilon with acute */
199 {'\346', "--.."}, /* ζ, zeta */
200 {'\347', "...."}, /* η, eta */
201 {'\336', "...."}, /* ή, eta with acute */
202 {'\350', "-.-."}, /* θ, theta */
203 {'\351', ".."}, /* ι, iota */
204 {'\337', ".."}, /* ί, iota with acute */
205 {'\372', ".."}, /* ϊ, iota with diaeresis */
206 {'\300', ".."}, /* ΐ, iota with acute and diaeresis */
207 {'\352', "-.-"}, /* κ, kappa */
208 {'\353', ".-.."}, /* λ, lambda */
209 {'\354', "--"}, /* μ, mu */
210 {'\355', "-."}, /* ν, nu */
211 {'\356', "-..-"}, /* ξ, xi */
212 {'\357', "---"}, /* ο, omicron */
213 {'\374', "---"}, /* ό, omicron with acute */
214 {'\360', ".--."}, /* π, pi */
215 {'\361', ".-."}, /* ρ, rho */
216 {'\363', "..."}, /* σ, sigma */
217 {'\362', "..."}, /* ς, final sigma */
218 {'\364', "-"}, /* τ, tau */
219 {'\365', "-.--"}, /* υ, upsilon */
220 {'\375', "-.--"}, /* ύ, upsilon with acute */
221 {'\373', "-.--"}, /* ϋ, upsilon and diaeresis */
222 {'\340', "-.--"}, /* ΰ, upsilon with acute and diaeresis */
223 {'\366', "..-."}, /* φ, phi */
224 {'\367', "----"}, /* χ, chi */
225 {'\370', "--.-"}, /* ψ, psi */
226 {'\371', ".--"}, /* ω, omega */
227 {'\376', ".--"}, /* ώ, omega with acute */
233 * Code-points for the Cyrillic alphabet in KOI8-R encoding.
234 * UTF-8 encoded chars in the comments.
236 static const struct morsetab koi8rtab[] = {
237 {'\301', ".-"}, /* а, a */
238 {'\302', "-..."}, /* б, be */
239 {'\327', ".--"}, /* в, ve */
240 {'\307', "--."}, /* г, ge */
241 {'\304', "-.."}, /* д, de */
242 {'\305', "."}, /* е, ye */
243 {'\243', "."}, /* ё, yo, the same as ye */
244 {'\326', "...-"}, /* ж, she */
245 {'\332', "--.."}, /* з, ze */
246 {'\311', ".."}, /* и, i */
247 {'\312', ".---"}, /* й, i kratkoye */
248 {'\313', "-.-"}, /* к, ka */
249 {'\314', ".-.."}, /* л, el */
250 {'\315', "--"}, /* м, em */
251 {'\316', "-."}, /* н, en */
252 {'\317', "---"}, /* о, o */
253 {'\320', ".--."}, /* п, pe */
254 {'\322', ".-."}, /* р, er */
255 {'\323', "..."}, /* с, es */
256 {'\324', "-"}, /* т, te */
257 {'\325', "..-"}, /* у, u */
258 {'\306', "..-."}, /* ф, ef */
259 {'\310', "...."}, /* х, kha */
260 {'\303', "-.-."}, /* ц, ce */
261 {'\336', "---."}, /* ч, che */
262 {'\333', "----"}, /* ш, sha */
263 {'\335', "--.-"}, /* щ, shcha */
264 {'\331', "-.--"}, /* ы, yi */
265 {'\330', "-..-"}, /* ь, myakhkij znak */
266 {'\334', "..-.."}, /* э, ae */
267 {'\300', "..--"}, /* ю, yu */
268 {'\321', ".-.-"}, /* я, ya */
273 static void show(const char *), play(const char *), morse(char);
274 static void decode (char *), fdecode(FILE *);
275 static void ttyout(const char *);
276 static void sighandler(int);
278 static int pflag, lflag, rflag, sflag, eflag;
279 static int wpm = 20; /* effective words per minute */
280 static int cpm; /* effective words per minute between
282 #define FREQUENCY 600
283 static int freq = FREQUENCY;
284 static char *device; /* for tty-controlled generator */
288 #define WORD_SPACE (7 - CHAR_SPACE - 1)
289 static float dot_clock;
290 static float cdot_clock;
291 static int spkr, line;
292 static struct termios otty, ntty;
297 #define GETOPTOPTS "c:d:ef:lprsw:"
299 "usage: morse [-elprs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
301 #define GETOPTOPTS "c:d:ef:lrsw:"
303 "usage: morse [-elrs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
307 static const struct morsetab *hightab;
310 main(int argc, char *argv[])
315 while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
325 setvbuf(stdout, 0, _IONBF, 0);
351 if ((sflag && lflag) || (sflag && rflag) || (lflag && rflag)) {
352 errx(1, "morse: only one of -l, -s, and -r allowed\n");
354 if ((pflag || device) && (sflag || lflag)) {
355 errx(1, "morse: only one of -p, -d and -l, -s allowed\n");
360 if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) {
361 errx(1, "morse: insane speed\n");
363 if ((pflag || device) && (freq == 0)) {
368 if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
374 if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
375 err(1, "open tty line");
377 if (tcgetattr(line, &otty) == -1) {
378 err(1, "tcgetattr() failed");
381 ntty.c_cflag |= CLOCAL;
382 tcsetattr(line, TCSANOW, &ntty);
383 lflags = fcntl(line, F_GETFL);
384 lflags &= ~O_NONBLOCK;
385 fcntl(line, F_SETFL, &lflags);
386 ioctl(line, TIOCMGET, &lflags);
387 lflags &= ~TIOCM_RTS;
389 ioctl(line, TIOCMSET, &lflags);
390 (void)signal(SIGHUP, sighandler);
391 (void)signal(SIGINT, sighandler);
392 (void)signal(SIGQUIT, sighandler);
393 (void)signal(SIGTERM, sighandler);
395 if (pflag || device) {
396 dot_clock = wpm / 2.4; /* dots/sec */
397 dot_clock = 1 / dot_clock; /* duration of a dot */
398 dot_clock = dot_clock / 2; /* dot_clock runs at twice */
400 dot_clock = dot_clock * 100; /* scale for ioctl */
402 cdot_clock = cpm / 2.4; /* dots/sec */
403 cdot_clock = 1 / cdot_clock; /* duration of a dot */
404 cdot_clock = cdot_clock / 2; /* dot_clock runs at twice */
406 cdot_clock = cdot_clock * 100; /* scale for ioctl */
412 if (setlocale(LC_CTYPE, "") != NULL &&
413 *(codeset = nl_langinfo(CODESET)) != '\0') {
414 if (strcmp(codeset, "KOI8-R") == 0)
416 else if (strcmp(codeset, "ISO8859-1") == 0 ||
417 strcmp(codeset, "ISO8859-15") == 0)
418 hightab = iso8859_1tab;
419 else if (strcmp(codeset, "ISO8859-7") == 0)
420 hightab = iso8859_7tab;
429 p = strtok(*argv, DELIMITERS);
436 p = strtok(NULL, DELIMITERS);
447 for (p = *argv; *p; ++p) {
457 while ((ch = getchar()) != EOF) {
464 tcsetattr(line, TCSANOW, &otty);
471 const struct morsetab *m;
473 if (isalpha((unsigned char)c))
474 c = tolower((unsigned char)c);
475 if ((c == '\r') || (c == '\n'))
488 for (m = ((unsigned char)c < 0x80? mtab: hightab);
489 m != NULL && m->inchar != '\0';
491 if (m->inchar == c) {
511 printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" :
523 for (c = s; *c != '\0'; c++) {
526 sound.frequency = freq;
527 sound.duration = dot_clock;
530 sound.frequency = freq;
531 sound.duration = dot_clock * DASH_LEN;
535 sound.duration = cdot_clock * WORD_SPACE;
540 if (sound.duration) {
541 if (ioctl(spkr, SPKRTONE, &sound) == -1) {
542 err(1, "ioctl play");
546 sound.duration = dot_clock;
547 if (ioctl(spkr, SPKRTONE, &sound) == -1) {
548 err(1, "ioctl rest");
552 sound.duration = cdot_clock * CHAR_SPACE;
553 ioctl(spkr, SPKRTONE, &sound);
558 ttyout(const char *s)
561 int duration, on, lflags;
563 for (c = s; *c != '\0'; c++) {
567 duration = dot_clock;
571 duration = dot_clock * DASH_LEN;
575 duration = cdot_clock * WORD_SPACE;
582 ioctl(line, TIOCMGET, &lflags);
584 ioctl(line, TIOCMSET, &lflags);
589 ioctl(line, TIOCMGET, &lflags);
590 lflags &= ~TIOCM_RTS;
591 ioctl(line, TIOCMSET, &lflags);
592 duration = dot_clock * 10000;
595 duration = cdot_clock * CHAR_SPACE * 10000;
600 fdecode(FILE *stream)
606 while (fgets(s, BUFSIZ - (s - buf), stream)) {
609 while (*p && isblank(*p)) {
612 while (*p && isspace(*p)) {
617 n = strpbrk(p, WHITESPACE);
619 /* The token was interrupted at the end
620 * of the buffer. Shift it to the begin
623 for (s = buf; *p; *s++ = *p++)
640 const struct morsetab *m;
643 for (m = mtab; m != NULL && m->inchar != '\0'; m++) {
644 if (strcmp(m->morse, p) == 0) {
651 for (m = hightab; m != NULL && m->inchar != '\0'; m++) {
652 if (strcmp(m->morse, p) == 0) {
662 sighandler(int signo)
665 ioctl(line, TIOCMSET, &olflags);
666 tcsetattr(line, TCSANOW, &otty);
668 signal(signo, SIG_DFL);
669 (void)kill(getpid(), signo);