|
|
nono 0.3.0
#include "header.h"
#include "autofd.h"
#include "kevent.h"
#include <queue>
#include <string>
#include <vector>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
//#define DEFAULT_DEV "/dev/ttyS0"
#define DEFAULT_DEV "/dev/dty00"
// ナノ秒に対するスケール。
// ミリ秒単位になるようなスケール値とする
#define DEFAULT_TIMESCALE (1000LL * 1000LL)
static long timescale = DEFAULT_TIMESCALE;
static bool opt_n = false;
[[noreturn]]
static void
usage()
{
printf(
"%s [-d devname] [-t timescale] file\n"
" -d devname: tty device (default:" DEFAULT_DEV ")\n"
" -t timescale: timescale factor (default:%lld)\n"
" -n: no transeive mode\n",
getprogname(),
DEFAULT_TIMESCALE
);
exit(1);
}
static void
Do(FILE *fp, int tty_fd)
{
autofd kq;
struct kevent kev;
timespec timeout;
int msec;
int wait;
int64 t;
int r;
std::queue<uint8> sbuf;
std::vector<uint8> rbuf;
kq = kqueue();
if (kq == -1) {
err(1, "kqueue");
}
if (tty_fd >= 0) {
r = kevent_add(kq, tty_fd, EVFILT_READ, EV_ADD, 0);
if (r == -1) {
err(1, "kevent_add(READ)");
}
r = kevent_add(kq, tty_fd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 1);
if (r == -1) {
err(1, "kevent_add(WRITE)");
}
}
// 受信タイムアウトデフォルト 100msec
int64 recv_timeout = 1 * 100 * 1000 * 1000;
char buf[1024];
bool complete = false;
bool prompt = true;
bool writable = true;
for (;;) {
if (prompt && !complete) {
if (fgets(buf, sizeof(buf), fp) != NULL) {
if (strncmp(buf, "#", 1) == 0) {
printf("%s", buf);
continue;
}
// ウェイト時間はデフォルトでミリ秒で指定
if (sscanf(buf, "@%d", &wait)) {
t = wait * timescale;
timeout.tv_sec = t / (1000 * 1000 * 1000);
timeout.tv_nsec = t % (1000 * 1000 * 1000);
nanosleep(&timeout, NULL);
continue;
} else if (sscanf(buf, "@R%d", &msec)) {
// 受信タイムアウト変更
recv_timeout = msec * 1000 * 1000;
continue;
} else {
for (char *p = buf; *p; p++) {
if (*p == '\n') {
sbuf.push('\r');
} else {
sbuf.push(*p);
}
}
prompt = false;
}
} else {
complete = true;
}
}
if (writable && sbuf.size() > 0) {
buf[0] = sbuf.front();
sbuf.pop();
r = write(tty_fd, buf, 1);
if (r <= 0) {
err(1, "tty_fd write");
}
writable = false;
}
t = recv_timeout;
timeout.tv_sec = t / (1000 * 1000 * 1000);
timeout.tv_nsec = t % (1000 * 1000 * 1000);
r = kevent_poll(kq, &kev, 1, &timeout);
if (r == -1) {
err(1, "kevent_poll");
}
if (r == 0) {
break;
}
int udata = EV_UDATA2INT(kev.udata);
if (udata == 0) {
r = read(tty_fd, buf, 1);
if (r < 0) {
err(1, "tty read");
}
if (r == 0) {
break;
}
// CR -> LF, LF -> discard
if (buf[0] == '\n') {
rbuf.push_back('\n');
fwrite(rbuf.data(), 1, rbuf.size(), stdout);
fflush(stdout);
rbuf.clear();
} else if (buf[0] == '\r') {
} else {
rbuf.push_back(buf[0]);
}
if (rbuf.size() == 1 && rbuf[0] == '>') {
prompt = true;
}
writable = true;
}
}
if (!complete) {
errx(1, "timeout");
}
}
int
main(int ac, char *av[])
{
int c;
std::string dev = DEFAULT_DEV;
while ((c = getopt(ac, av, "d:nt:")) >= 0) {
switch (c) {
case 'd':
dev = optarg;
break;
case 'n':
opt_n = true;
break;
case 't':
timescale = atol(optarg);
break;
default:
usage();
break;
}
}
ac -= optind;
av += optind;
if (ac == 0) {
usage();
}
FILE *fp;
if (strcmp(av[0], "-") == 0) {
fp = stdin;
} else {
fp = fopen(av[0], "r");
if (fp == NULL) {
err(1, "fopen %s", av[0]);
}
}
int tty_fd = -1;
if (!opt_n) {
struct termios t;
tty_fd = open(dev.c_str(), O_RDWR);
if (tty_fd < 0) {
err(1, "open %s", dev.c_str());
}
tcgetattr(tty_fd, &t);
cfmakeraw(&t);
cfsetspeed(&t, B9600);
t.c_iflag |= IGNBRK;
t.c_oflag &= ~(OPOST);
t.c_cflag |= CREAD | CLOCAL | HUPCL | CS8;
t.c_lflag &= ~(ECHO | ICANON);
t.c_cc[VTIME] = 0;
t.c_cc[VMIN] = 1;
tcsetattr(tty_fd, TCSAFLUSH, &t);
}
Do(fp, tty_fd);
if (tty_fd >= 0) {
close(tty_fd);
}
fclose(fp);
return 0;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.