-/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic -Wno-format-truncation $@ -o $* \
- -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -lncurses -ltinfo -lreadline $+
- * Written by Emil.
- * Licensed under the GPLv3 only.
- *
- * TODO Add proper editing facilities
- * TODO Print sent messages
- */
+/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -Wpedantic -Wno-format-truncation $@ -o $* \
+ -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -lncurses -ltinfo $+
+ * Written by Emil.
+ * Licensed under the GPLv3 only.
+ *
+ * TODO Add proper editing facilities
+ */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
+#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <ncurses.h>
-#include <readline/readline.h>
#define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"
#define PORT "50000"
+#define TAB 9 /* */
#define BACKSPACE 263 /* ^G */
+#define C_C 4 /* ... */
+#define C_D 4 /* quit */
+#define C_A 1 /* BOL */
+#define C_B 2 /* BOL */
+#define C_E 5 /* EOL */
+#define C_U 21 /* CLR TO BOL */
+#define C_H 8 /* BACKSPACE */
+#define C_W 23 /* DELETE PREVWORD */
+#define C_L 12 /* Signal SIGWINCH */
+#define C_R 18 /* hah not going to implement that */
+
+#define UP 258
+#define DOWN 259
+#define LEFT 260
+#define RIGHT 261
#define TIMESTR "<%Y/%m/%d %H:%M:%S "
#define TIMESTRMAX 21
#define SENDMAX (1 << 8) - 1
-#define RECVMAX (1 << 11)
+#define RECVMAX 17663 /* (1 << 11) */
#define streq(a,b) (!strcmp(a,b))
#define strneq(a,b,c) (!memcmp(a,b,c))
int g_y, g_x;
int g_sockfd = -1;
+void fillline(int y, int xstart, char c) {
+ int i = xstart, x = g_x;
+ for (; i < x; ++i)
+ { mvaddch(y,i,c); }
+}
+
+#define TITLE(str) do { mvprintw(0,0,str); fillline(0,strlen(str),'-'); } while (0)
+
+void hardrefresh(void) {
+ mvprintw(g_y - 2, 0, "max screen %d; max ours %d", (g_y - 3) * SENDMAX, RECVMAX);
+ TITLE("-- MOONTALK ");
+ fillline(g_y - 3, 0, '-');
+}
+
+#undef TITLE
+
+int g_hardrefresh = 0;
+
void init_screen(int x) {
(void)x;
signal(SIGWINCH, SIG_IGN);
g_y = w.ws_row;
g_x = w.ws_col;
initscr();
- cbreak();
+ raw();
noecho();
keypad(stdscr, ERR);
nodelay(stdscr, TRUE);
+ ESCDELAY = 0;
clear();
+ hardrefresh();
+ g_hardrefresh = 1;
signal(SIGWINCH, init_screen);
}
-void free_connection(void) {
- close(g_sockfd);
+void stop(void) {
+ int sockfd = g_sockfd;
+ endwin();
+ printf("stopping now. maybe you forgot torify?\n");
+ if (sockfd > -1) { close(sockfd); }
g_sockfd = -1;
}
struct addrinfo hints, * res;
memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
- hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) {
- fprintf(stderr, "init_connection: %s\n", gai_strerror(status));
+ perror("init_connection");
exit(1);
}
g_sockfd = sockfd;
return sockfd;
error:
+ if (sockfd > -1) { close(sockfd); }
perror("init_connection");
exit(1);
__builtin_unreachable();
return i;
}
+size_t getoff(char * buf, size_t len, size_t count, char c) {
+ size_t i;
+ for (i = 0; count && i < len; ++i) {
+ count -= (buf[i] == c) * 1;
+ ++buf;
+ }
+ return i;
+}
+
size_t lastline(char * buf, size_t len) {
size_t ret;
char * start = buf;
return 1;
}
if (streq(*(argv-1), "-serv")) {
- printf("serv: %s\n", *argv);
serv = *argv;
}
else if (streq(*(argv-1), "-port")) {
- printf("port: %s\n", *argv);
port = *argv;
}
else if (streq(*(argv-1), "-name")) {
- printf("name: %s\n", *argv);
memset(name, 0, 31);
strncpy(name, *argv, 31);
}
}
- /* atexit(void (*endwin)(void)); */
init_screen(-1);
+ move(1,0);
printw("Connecting to %s:%s as %s\n", serv, port, name);
printw("g_y: %d; g_x: %d\n", g_y, g_x);
refresh();
clear();
+ atexit(stop);
sockfd = init_connection(serv, port);
char raw[SENDMAX + RECVMAX];
char * sendbuf = raw, * recvbuf = raw + SENDMAX;
size_t sendminlen;
- size_t sendlen = sendminlen, recvlen = 0;
+ size_t sendlen = sendminlen, recvlen = 1;
time_t t;
struct tm * tm;
- int frame = 15.;
- float interval = 1. / frame;
+ useconds_t frame = 25;
+ useconds_t interval = 1000000 / frame;
int32_t ct = 0;
+ int lc, off;
int ch;
int ret;
sendminlen += 2;
sendlen = sendminlen;
- rl_bind_key('\t', rl_insert);
- rl_catch_signals = 0;
- rl_catch_sigwinch = 0;
- rl_prep_term_function = NULL;
- rl_deprep_term_function = NULL;
- rl_change_environment = 0;
-
- #if 0
- rl_getc_function = +[](FILE* ignore){
- input_available = false;
- return (int)input;
- };
- rl_input_available_hook = +[]{
- return input_available;
- };
- rl_redisplay_function = +[]{
- wmove(myWindow, 1, 1);
- wclrtoeol(myWindow);
- box(myWindow, 0, 0);
- waddstr(myWindow, rl_line_buffer);
- wrefresh(myWindow);
- return;
- };
- rl_callback_handler_install("", +[](char *line){
- wmove(stdscr, 0, 0);
- wclrtoeol(stdscr);
- addstr(line);
- refresh();
- return;
- });
- #endif
-
+ hardrefresh();
+ mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
while (1) {
/* update */
- if (ct % frame == 0) {
+ if (ct % (frame) == 0) {
UPDATE_TIME();
+ mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
}
if (ct % (frame * 2) == 0) {
ret = recv(sockfd, recvbuf + recvlen, RECVMAX - recvlen, MSG_DONTWAIT);
- recvlen += (ret > 0) * ret;
- mvaddnstr(0, 0, recvbuf, recvlen);
- mvprintw(g_y - 2, 0, "recvlen %ld", recvlen);
+ if (ret > 0 || g_hardrefresh) {
+ recvlen += ret;
+ lc = countlines(recvbuf, recvlen);
+ off = lc - g_y - 4;
+ off = getoff(recvbuf, recvlen, (off > 0) * off, '\n');
+ mvaddnstr(1, 0, recvbuf + off, recvlen - off);
+ g_hardrefresh = 0;
+ }
}
/* send */
- ch = getch();
- if (ch == -1);
- else if (ch == '\n') {
- if (sendlen == sendminlen) { continue; }
- sendbuf[sendlen++] = '\n'; /* terminator */
- send(sockfd, sendbuf, sendlen, 0);
- memset(sendbuf + sendminlen, 0, sendlen - sendminlen);
- sendlen = sendminlen;
- clearline(g_y - 1);
- }
- else if (ch == BACKSPACE) {
- clearline(g_y - 1);
- if (sendlen - 1 >= sendminlen)
- { --sendlen; }
+ while ((ch = getch()) != -1) {
+ if (ch == '\n') {
+ if (sendlen == sendminlen) { continue; }
+ UPDATE_TIME();
+ sendbuf[sendlen++] = '\n'; /* terminator */
+ send(sockfd, sendbuf, sendlen, 0);
+ memcpy(recvbuf + recvlen, sendbuf, (sendlen + recvlen < RECVMAX) * sendlen);
+ recvlen += sendlen;
+ mvaddnstr(1, 0, recvbuf, recvlen);
+ mvaddch(g_y - 1, sendminlen, ' ');
+ clearline(g_y - 1);
+ sendlen = sendminlen;
+ }
+ else if (ch == C_D) {
+ exit(0);
+ }
+ else if (ch == BACKSPACE) {
+ clearline(g_y - 1);
+ if (sendlen - 1 >= sendminlen)
+ { mvaddch(g_y - 1, --sendlen, ' '); }
+ mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
+ move(g_y - 1, sendlen);
+ }
+ else if (ch > 31 && ch < 127) {
+ if (sendlen + 1 < SENDMAX)
+ { sendbuf[sendlen++] = ch; }
+ mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
+ }
}
- else if (ch > 31 && ch < 127) {
- if (sendlen + 1 < SENDMAX)
- { sendbuf[sendlen++] = ch; }
- }
- mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
move(g_y - 1, sendlen);
- /* sleep */
++ct;
- sleep(interval);
refresh();
+ usleep(interval);
}
+ return 0;
}
--- /dev/null
+/* blackhole.c - eats incoming messages, does nothing, just for testing
+ * @BAKE cc -O2 -std=gnu89 -Wall -Wextra -Wpedantic $@ -o $*
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#define PORT "91991"
+#define BACKLOG 1
+
+void sigchld_handler(int s) {
+ int saved_errno = errno;
+ (void)s;
+ while(waitpid(-1, NULL, WNOHANG) > 0);
+ errno = saved_errno;
+}
+
+void *get_in_addr(struct sockaddr *sa) {
+ if (sa->sa_family == AF_INET) {
+ return &(((struct sockaddr_in*)sa)->sin_addr);
+ }
+
+ return &(((struct sockaddr_in6*)sa)->sin6_addr);
+}
+
+int main (void) {
+ int sockfd, connfd, rv, yes = 1;
+ struct addrinfo hints, *servinfo, *p;
+ struct sockaddr_storage their_addr;
+ char s[INET6_ADDRSTRLEN];
+ socklen_t sin_size;
+ struct sigaction sa;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+ return 1;
+ }
+
+ for(p = servinfo; p != NULL; p = p->ai_next) {
+ if ((sockfd = socket(p->ai_family, p->ai_socktype,
+ p->ai_protocol)) == -1) {
+ perror("server: socket");
+ continue;
+ }
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
+ sizeof(int)) == -1) {
+ perror("setsockopt");
+ exit(1);
+ }
+ if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+ close(sockfd);
+ perror("server: bind");
+ continue;
+ }
+ break;
+ }
+
+ freeaddrinfo(servinfo);
+
+ if (p == NULL) {
+ fprintf(stderr, "server: failed to bind\n");
+ exit(1);
+ }
+
+ if (listen(sockfd, BACKLOG) == -1) {
+ perror("listen");
+ exit(1);
+ }
+
+ sa.sa_handler = sigchld_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ if (sigaction(SIGCHLD, &sa, NULL) == -1) {
+ perror("sigaction");
+ exit(1);
+ }
+
+ printf("server: waiting for connections...\n");
+
+ while (1) {
+ sin_size = sizeof their_addr;
+ connfd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
+
+ if (connfd == -1) {
+ perror("accept");
+ continue;
+ }
+
+ inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s);
+
+ printf("server: got connection from %s\n", s);
+
+ if (!fork()) {
+ close(sockfd);
+ while (1) { sleep(5); }
+ close(connfd);
+ exit(0);
+ }
+ close(connfd);
+ }
+ close(sockfd);
+ sockfd = -1;
+ return 0;
+}