aboutsummaryrefslogtreecommitdiff
path: root/usr.bin/morse/morse.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/morse/morse.c')
-rw-r--r--usr.bin/morse/morse.c662
1 files changed, 662 insertions, 0 deletions
diff --git a/usr.bin/morse/morse.c b/usr.bin/morse/morse.c
new file mode 100644
index 000000000000..7d9fcde1e5e3
--- /dev/null
+++ b/usr.bin/morse/morse.c
@@ -0,0 +1,662 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Taught to send *real* morse by Lyndon Nerenberg (VE6BBM)
+ * <lyndon@orthanc.ca>
+ */
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifdef __FreeBSD__
+/* Always use the speaker, let the open fail if -p is selected */
+#define SPEAKER "/dev/speaker"
+#endif
+
+#define WHITESPACE " \t\n"
+#define DELIMITERS " \t"
+
+#ifdef SPEAKER
+#include <dev/speaker/speaker.h>
+#endif
+
+struct morsetab {
+ const char inchar;
+ const char *morse;
+};
+
+static const struct morsetab mtab[] = {
+
+ /* letters */
+
+ {'a', ".-"},
+ {'b', "-..."},
+ {'c', "-.-."},
+ {'d', "-.."},
+ {'e', "."},
+ {'f', "..-."},
+ {'g', "--."},
+ {'h', "...."},
+ {'i', ".."},
+ {'j', ".---"},
+ {'k', "-.-"},
+ {'l', ".-.."},
+ {'m', "--"},
+ {'n', "-."},
+ {'o', "---"},
+ {'p', ".--."},
+ {'q', "--.-"},
+ {'r', ".-."},
+ {'s', "..."},
+ {'t', "-"},
+ {'u', "..-"},
+ {'v', "...-"},
+ {'w', ".--"},
+ {'x', "-..-"},
+ {'y', "-.--"},
+ {'z', "--.."},
+
+ /* digits */
+
+ {'0', "-----"},
+ {'1', ".----"},
+ {'2', "..---"},
+ {'3', "...--"},
+ {'4', "....-"},
+ {'5', "....."},
+ {'6', "-...."},
+ {'7', "--..."},
+ {'8', "---.."},
+ {'9', "----."},
+
+ /* punctuation */
+
+ {',', "--..--"},
+ {'.', ".-.-.-"},
+ {'"', ".-..-."},
+ {'!', "..--."},
+ {'?', "..--.."},
+ {'/', "-..-."},
+ {'-', "-....-"},
+ {'=', "-...-"}, /* BT */
+ {':', "---..."},
+ {';', "-.-.-."},
+ {'(', "-.--."}, /* KN */
+ {')', "-.--.-"},
+ {'$', "...-..-"},
+ {'+', ".-.-."}, /* AR */
+ {'@', ".--.-."}, /* AC */
+ {'_', "..--.-"},
+ {'\'', ".----."},
+
+ /* prosigns without already assigned values */
+
+ {'#', ".-..."}, /* AS */
+ {'&', "...-.-"}, /* SK */
+ {'*', "...-."}, /* VE */
+ {'%', "-...-.-"}, /* BK */
+
+ {'\0', ""}
+};
+
+/*
+ * Code-points for some Latin1 chars in ISO-8859-1 encoding.
+ * UTF-8 encoded chars in the comments.
+ */
+static const struct morsetab iso8859_1tab[] = {
+ {'\340', ".--.-"}, /* à */
+ {'\341', ".--.-"}, /* á */
+ {'\342', ".--.-"}, /* â */
+ {'\344', ".-.-"}, /* ä */
+ {'\347', "-.-.."}, /* ç */
+ {'\350', "..-.."}, /* è */
+ {'\351', "..-.."}, /* é */
+ {'\352', "-..-."}, /* ê */
+ {'\361', "--.--"}, /* ñ */
+ {'\366', "---."}, /* ö */
+ {'\374', "..--"}, /* ü */
+
+ {'\0', ""}
+};
+
+/*
+ * Code-points for some Greek chars in ISO-8859-7 encoding.
+ * UTF-8 encoded chars in the comments.
+ */
+static const struct morsetab iso8859_7tab[] = {
+ /*
+ * This table does not implement:
+ * - the special sequences for the seven diphthongs,
+ * - the punctuation differences.
+ * Implementing these features would introduce too many
+ * special-cases in the program's main loop.
+ * The diphthong sequences are:
+ * alpha iota .-.-
+ * alpha upsilon ..--
+ * epsilon upsilon ---.
+ * eta upsilon ...-
+ * omicron iota ---..
+ * omicron upsilon ..-
+ * upsilon iota .---
+ * The different punctuation symbols are:
+ * ; ..-.-
+ * ! --..--
+ */
+ {'\341', ".-"}, /* α, alpha */
+ {'\334', ".-"}, /* ά, alpha with acute */
+ {'\342', "-..."}, /* β, beta */
+ {'\343', "--."}, /* γ, gamma */
+ {'\344', "-.."}, /* δ, delta */
+ {'\345', "."}, /* ε, epsilon */
+ {'\335', "."}, /* έ, epsilon with acute */
+ {'\346', "--.."}, /* ζ, zeta */
+ {'\347', "...."}, /* η, eta */
+ {'\336', "...."}, /* ή, eta with acute */
+ {'\350', "-.-."}, /* θ, theta */
+ {'\351', ".."}, /* ι, iota */
+ {'\337', ".."}, /* ί, iota with acute */
+ {'\372', ".."}, /* ϊ, iota with diaeresis */
+ {'\300', ".."}, /* ΐ, iota with acute and diaeresis */
+ {'\352', "-.-"}, /* κ, kappa */
+ {'\353', ".-.."}, /* λ, lambda */
+ {'\354', "--"}, /* μ, mu */
+ {'\355', "-."}, /* ν, nu */
+ {'\356', "-..-"}, /* ξ, xi */
+ {'\357', "---"}, /* ο, omicron */
+ {'\374', "---"}, /* ό, omicron with acute */
+ {'\360', ".--."}, /* π, pi */
+ {'\361', ".-."}, /* ρ, rho */
+ {'\363', "..."}, /* σ, sigma */
+ {'\362', "..."}, /* ς, final sigma */
+ {'\364', "-"}, /* τ, tau */
+ {'\365', "-.--"}, /* υ, upsilon */
+ {'\375', "-.--"}, /* ύ, upsilon with acute */
+ {'\373', "-.--"}, /* ϋ, upsilon and diaeresis */
+ {'\340', "-.--"}, /* ΰ, upsilon with acute and diaeresis */
+ {'\366', "..-."}, /* φ, phi */
+ {'\367', "----"}, /* χ, chi */
+ {'\370', "--.-"}, /* ψ, psi */
+ {'\371', ".--"}, /* ω, omega */
+ {'\376', ".--"}, /* ώ, omega with acute */
+
+ {'\0', ""}
+};
+
+/*
+ * Code-points for the Cyrillic alphabet in KOI8-R encoding.
+ * UTF-8 encoded chars in the comments.
+ */
+static const struct morsetab koi8rtab[] = {
+ {'\301', ".-"}, /* а, a */
+ {'\302', "-..."}, /* б, be */
+ {'\327', ".--"}, /* в, ve */
+ {'\307', "--."}, /* г, ge */
+ {'\304', "-.."}, /* д, de */
+ {'\305', "."}, /* е, ye */
+ {'\243', "."}, /* ё, yo, the same as ye */
+ {'\326', "...-"}, /* ж, she */
+ {'\332', "--.."}, /* з, ze */
+ {'\311', ".."}, /* и, i */
+ {'\312', ".---"}, /* й, i kratkoye */
+ {'\313', "-.-"}, /* к, ka */
+ {'\314', ".-.."}, /* л, el */
+ {'\315', "--"}, /* м, em */
+ {'\316', "-."}, /* н, en */
+ {'\317', "---"}, /* о, o */
+ {'\320', ".--."}, /* п, pe */
+ {'\322', ".-."}, /* р, er */
+ {'\323', "..."}, /* с, es */
+ {'\324', "-"}, /* т, te */
+ {'\325', "..-"}, /* у, u */
+ {'\306', "..-."}, /* ф, ef */
+ {'\310', "...."}, /* х, kha */
+ {'\303', "-.-."}, /* ц, ce */
+ {'\336', "---."}, /* ч, che */
+ {'\333', "----"}, /* ш, sha */
+ {'\335', "--.-"}, /* щ, shcha */
+ {'\331', "-.--"}, /* ы, yi */
+ {'\330', "-..-"}, /* ь, myakhkij znak */
+ {'\334', "..-.."}, /* э, ae */
+ {'\300', "..--"}, /* ю, yu */
+ {'\321', ".-.-"}, /* я, ya */
+
+ {'\0', ""}
+};
+
+static void show(const char *), play(const char *), morse(char);
+static void decode (char *), fdecode(FILE *);
+static void ttyout(const char *);
+static void sighandler(int);
+
+static int pflag, lflag, rflag, sflag, eflag;
+static int wpm = 20; /* effective words per minute */
+static int cpm; /* effective words per minute between
+ * characters */
+#define FREQUENCY 600
+static int freq = FREQUENCY;
+static char *device; /* for tty-controlled generator */
+
+#define DASH_LEN 3
+#define CHAR_SPACE 3
+#define WORD_SPACE (7 - CHAR_SPACE - 1)
+static float dot_clock;
+static float cdot_clock;
+static int spkr, line;
+static struct termios otty, ntty;
+static int olflags;
+
+#ifdef SPEAKER
+static tone_t sound;
+#define GETOPTOPTS "c:d:ef:lprsw:"
+#define USAGE \
+"usage: morse [-elprs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
+#else
+#define GETOPTOPTS "c:d:ef:lrsw:"
+#define USAGE \
+"usage: morse [-elrs] [-d device] [-w speed] [-c speed] [-f frequency] [string ...]\n"
+
+#endif
+
+static const struct morsetab *hightab;
+
+int
+main(int argc, char *argv[])
+{
+ int ch, lflags;
+ char *p, *codeset;
+
+ while ((ch = getopt(argc, argv, GETOPTOPTS)) != -1)
+ switch ((char) ch) {
+ case 'c':
+ cpm = atoi(optarg);
+ break;
+ case 'd':
+ device = optarg;
+ break;
+ case 'e':
+ eflag = 1;
+ setvbuf(stdout, 0, _IONBF, 0);
+ break;
+ case 'f':
+ freq = atoi(optarg);
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+#ifdef SPEAKER
+ case 'p':
+ pflag = 1;
+ break;
+#endif
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'w':
+ wpm = atoi(optarg);
+ break;
+ case '?':
+ default:
+ errx(1, USAGE);
+ }
+ if ((sflag && lflag) || (sflag && rflag) || (lflag && rflag)) {
+ errx(1, "morse: only one of -l, -s, and -r allowed\n");
+ }
+ if ((pflag || device) && (sflag || lflag)) {
+ errx(1, "morse: only one of -p, -d and -l, -s allowed\n");
+ }
+ if (cpm == 0) {
+ cpm = wpm;
+ }
+ if ((pflag || device) && ((wpm < 1) || (wpm > 60) || (cpm < 1) || (cpm > 60))) {
+ errx(1, "morse: insane speed\n");
+ }
+ if ((pflag || device) && (freq == 0)) {
+ freq = FREQUENCY;
+ }
+#ifdef SPEAKER
+ if (pflag) {
+ if ((spkr = open(SPEAKER, O_WRONLY, 0)) == -1) {
+ err(1, SPEAKER);
+ }
+ } else
+#endif
+ if (device) {
+ if ((line = open(device, O_WRONLY | O_NONBLOCK)) == -1) {
+ err(1, "open tty line");
+ }
+ if (tcgetattr(line, &otty) == -1) {
+ err(1, "tcgetattr() failed");
+ }
+ ntty = otty;
+ ntty.c_cflag |= CLOCAL;
+ tcsetattr(line, TCSANOW, &ntty);
+ lflags = fcntl(line, F_GETFL);
+ lflags &= ~O_NONBLOCK;
+ fcntl(line, F_SETFL, &lflags);
+ ioctl(line, TIOCMGET, &lflags);
+ lflags &= ~TIOCM_RTS;
+ olflags = lflags;
+ ioctl(line, TIOCMSET, &lflags);
+ (void)signal(SIGHUP, sighandler);
+ (void)signal(SIGINT, sighandler);
+ (void)signal(SIGQUIT, sighandler);
+ (void)signal(SIGTERM, sighandler);
+ }
+ if (pflag || device) {
+ dot_clock = wpm / 2.4; /* dots/sec */
+ dot_clock = 1 / dot_clock; /* duration of a dot */
+ dot_clock = dot_clock / 2; /* dot_clock runs at twice */
+ /* the dot rate */
+ dot_clock = dot_clock * 100; /* scale for ioctl */
+
+ cdot_clock = cpm / 2.4; /* dots/sec */
+ cdot_clock = 1 / cdot_clock; /* duration of a dot */
+ cdot_clock = cdot_clock / 2; /* dot_clock runs at twice */
+ /* the dot rate */
+ cdot_clock = cdot_clock * 100; /* scale for ioctl */
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (setlocale(LC_CTYPE, "") != NULL &&
+ *(codeset = nl_langinfo(CODESET)) != '\0') {
+ if (strcmp(codeset, "KOI8-R") == 0)
+ hightab = koi8rtab;
+ else if (strcmp(codeset, "ISO8859-1") == 0 ||
+ strcmp(codeset, "ISO8859-15") == 0)
+ hightab = iso8859_1tab;
+ else if (strcmp(codeset, "ISO8859-7") == 0)
+ hightab = iso8859_7tab;
+ }
+
+ if (lflag) {
+ printf("m");
+ }
+ if (rflag) {
+ if (*argv) {
+ do {
+ p = strtok(*argv, DELIMITERS);
+ if (p == NULL) {
+ decode(*argv);
+ }
+ else {
+ while (p) {
+ decode(p);
+ p = strtok(NULL, DELIMITERS);
+ }
+ }
+ } while (*++argv);
+ putchar('\n');
+ } else {
+ fdecode(stdin);
+ }
+ }
+ else if (*argv) {
+ do {
+ for (p = *argv; *p; ++p) {
+ if (eflag)
+ putchar(*p);
+ morse(*p);
+ }
+ if (eflag)
+ putchar(' ');
+ morse(' ');
+ } while (*++argv);
+ } else {
+ while ((ch = getchar()) != EOF) {
+ if (eflag)
+ putchar(ch);
+ morse(ch);
+ }
+ }
+ if (device)
+ tcsetattr(line, TCSANOW, &otty);
+ exit(0);
+}
+
+static void
+morse(char c)
+{
+ const struct morsetab *m;
+
+ if (isalpha((unsigned char)c))
+ c = tolower((unsigned char)c);
+ if ((c == '\r') || (c == '\n'))
+ c = ' ';
+ if (c == ' ') {
+ if (pflag)
+ play(" ");
+ else if (device)
+ ttyout(" ");
+ else if (lflag)
+ printf("\n");
+ else
+ show("");
+ return;
+ }
+ for (m = ((unsigned char)c < 0x80? mtab: hightab);
+ m != NULL && m->inchar != '\0';
+ m++) {
+ if (m->inchar == c) {
+ if (pflag) {
+ play(m->morse);
+ } else if (device) {
+ ttyout(m->morse);
+ } else
+ show(m->morse);
+ }
+ }
+}
+
+static void
+show(const char *s)
+{
+ if (lflag) {
+ printf("%s ", s);
+ } else if (sflag) {
+ printf(" %s\n", s);
+ } else {
+ for (; *s; ++s)
+ printf(" %s", *s == '.' ? *(s + 1) == '\0' ? "dit" :
+ "di" : "dah");
+ printf("\n");
+ }
+}
+
+static void
+play(const char *s)
+{
+#ifdef SPEAKER
+ const char *c;
+
+ for (c = s; *c != '\0'; c++) {
+ switch (*c) {
+ case '.':
+ sound.frequency = freq;
+ sound.duration = dot_clock;
+ break;
+ case '-':
+ sound.frequency = freq;
+ sound.duration = dot_clock * DASH_LEN;
+ break;
+ case ' ':
+ sound.frequency = 0;
+ sound.duration = cdot_clock * WORD_SPACE;
+ break;
+ default:
+ sound.duration = 0;
+ }
+ if (sound.duration) {
+ if (ioctl(spkr, SPKRTONE, &sound) == -1) {
+ err(1, "ioctl play");
+ }
+ }
+ sound.frequency = 0;
+ sound.duration = dot_clock;
+ if (ioctl(spkr, SPKRTONE, &sound) == -1) {
+ err(1, "ioctl rest");
+ }
+ }
+ sound.frequency = 0;
+ sound.duration = cdot_clock * CHAR_SPACE;
+ ioctl(spkr, SPKRTONE, &sound);
+#endif
+}
+
+static void
+ttyout(const char *s)
+{
+ const char *c;
+ int duration, on, lflags;
+
+ for (c = s; *c != '\0'; c++) {
+ switch (*c) {
+ case '.':
+ on = 1;
+ duration = dot_clock;
+ break;
+ case '-':
+ on = 1;
+ duration = dot_clock * DASH_LEN;
+ break;
+ case ' ':
+ on = 0;
+ duration = cdot_clock * WORD_SPACE;
+ break;
+ default:
+ on = 0;
+ duration = 0;
+ }
+ if (on) {
+ ioctl(line, TIOCMGET, &lflags);
+ lflags |= TIOCM_RTS;
+ ioctl(line, TIOCMSET, &lflags);
+ }
+ duration *= 10000;
+ if (duration)
+ usleep(duration);
+ ioctl(line, TIOCMGET, &lflags);
+ lflags &= ~TIOCM_RTS;
+ ioctl(line, TIOCMSET, &lflags);
+ duration = dot_clock * 10000;
+ usleep(duration);
+ }
+ duration = cdot_clock * CHAR_SPACE * 10000;
+ usleep(duration);
+}
+
+void
+fdecode(FILE *stream)
+{
+ char *n, *p, *s;
+ char buf[BUFSIZ];
+
+ s = buf;
+ while (fgets(s, BUFSIZ - (s - buf), stream)) {
+ p = buf;
+
+ while (*p && isblank(*p)) {
+ p++;
+ }
+ while (*p && isspace(*p)) {
+ p++;
+ putchar (' ');
+ }
+ while (*p) {
+ n = strpbrk(p, WHITESPACE);
+ if (n == NULL) {
+ /* The token was interrupted at the end
+ * of the buffer. Shift it to the begin
+ * of the buffer.
+ */
+ for (s = buf; *p; *s++ = *p++)
+ ;
+ } else {
+ *n = '\0';
+ n++;
+ decode(p);
+ p = n;
+ }
+ }
+ }
+ putchar('\n');
+}
+
+void
+decode(char *p)
+{
+ char c;
+ const struct morsetab *m;
+
+ c = ' ';
+ for (m = mtab; m != NULL && m->inchar != '\0'; m++) {
+ if (strcmp(m->morse, p) == 0) {
+ c = m->inchar;
+ break;
+ }
+ }
+
+ if (c == ' ')
+ for (m = hightab; m != NULL && m->inchar != '\0'; m++) {
+ if (strcmp(m->morse, p) == 0) {
+ c = m->inchar;
+ break;
+ }
+ }
+
+ putchar(c);
+}
+
+static void
+sighandler(int signo)
+{
+
+ ioctl(line, TIOCMSET, &olflags);
+ tcsetattr(line, TCSANOW, &otty);
+
+ signal(signo, SIG_DFL);
+ (void)kill(getpid(), signo);
+}