freebsd-dev/usr.bin/morse/morse.c
Li-Wen Hsu 917fa2a3e9 Unbreak build after r327614
Approved by:	mjg (on IRC)
Differential Revision:	https://reviews.freebsd.org/D13782
2018-01-06 09:48:04 +00:00

673 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*-
* 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>
*/
static const char copyright[] =
"@(#) Copyright (c) 1988, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#if 0
static char sccsid[] = "@(#)morse.c 8.1 (Berkeley) 5/31/93";
#endif
static const char rcsid[] =
"$FreeBSD$";
#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);
}