-/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic $@ -o $* -lncurses -ltinfo $+
- * Written by Emil.
- * Licensed under the GPLv3 only.
- */
+/* 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
+ */
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <ncurses.h>
+#include <readline/readline.h>
#define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"
#define PORT "50000"
+#define BACKSPACE 263 /* ^G */
+
+#define TIMESTR "<%Y/%m/%d %H:%M:%S "
+#define TIMESTRMAX 21
+
+#define SENDMAX (1 << 8) - 1
+#define RECVMAX (1 << 11)
+
#define streq(a,b) (!strcmp(a,b))
+#define strneq(a,b,c) (!memcmp(a,b,c))
-int g_row, g_col;
+int g_y, g_x;
+int g_sockfd = -1;
-void free_screen(void) {
- endwin();
-}
+void init_screen(int x) {
+ (void)x;
+ signal(SIGWINCH, SIG_IGN);
-void init_screen(void) {
+ endwin();
+ struct winsize w;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+ g_y = w.ws_row;
+ g_x = w.ws_col;
initscr();
cbreak();
noecho();
keypad(stdscr, ERR);
nodelay(stdscr, TRUE);
-}
-
-void handle_winch(int x) {
- (void)x;
- struct winsize w;
- signal(SIGWINCH, SIG_IGN);
-
- endwin();
- init_screen();
- refresh();
clear();
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
- g_row = w.ws_row;
- g_col = w.ws_col;
-
- signal(SIGWINCH, handle_winch);
+ signal(SIGWINCH, init_screen);
}
-int g_sockfd = -1;
-
void free_connection(void) {
close(g_sockfd);
g_sockfd = -1;
-
- /* the program should be at an end. If we're reconnecting, then at
- the top level (main) we'd free, even though the main's sockfd is now
- out-of-date, we can simply ignore that and take the result of socket
- init */
}
/* always returns an accessible socket */
__builtin_unreachable();
}
+#if 0
+int getanonval(char * buf, size_t len) {
+ return 0;
+}
+#endif
+
+int countlines(char * buf, size_t len) {
+ size_t i = 0;
+ for (buf += len - 1; *buf; --buf)
+ { i += (*buf == '\n'); }
+ return i;
+}
+
+size_t lastline(char * buf, size_t len) {
+ size_t ret;
+ char * start = buf;
+ for (buf += len - 1; *buf; --buf) {
+ ret = (*buf == '\n') ? buf - start : 0;
+ if (ret) { return ret; }
+ }
+ return 0;
+}
+
+void clearline(int y) {
+ int x = g_x;
+ int oldy, oldx;
+ getyx(stdscr, oldy, oldx);
+ move(y, x);
+ clrtoeol();
+ move(oldy, oldx);
+}
+
+#define UPDATE_TIME() do { \
+ t = time(NULL); \
+ tm = gmtime(&t); \
+ strftime(sendbuf, TIMESTRMAX, TIMESTR, tm); \
+ sendbuf[TIMESTRMAX - 1] = ' '; } while (0)
+
+
int main (int argc, char ** argv) {
char * argv0 = argv[0];
- char * serv = SERV, * port = PORT, * name = "anonymous";
-
- #define PREFIX_MAX 21
- char raw[(2 << 9) + (2 << 13)];
- char * sendbuf = raw + PREFIX_MAX, * recvbuf = raw + (2 << 9);
- size_t sendlen = 0, sendmax = (2 << 9) - PREFIX_MAX,
- recvlen = 0, recvmax = 2 << 13;
+ char * serv = SERV, * port = PORT, name[32] = "anonymous";
int sockfd;
}
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(free_screen);
- init_screen();
- signal(SIGWINCH, handle_winch);
+ /* atexit(void (*endwin)(void)); */
+ init_screen(-1);
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();
+
sockfd = init_connection(serv, port);
- float interval = 1 / 30;
- int ch;
+
+ char raw[SENDMAX + RECVMAX];
+ char * sendbuf = raw, * recvbuf = raw + SENDMAX;
+ size_t sendminlen;
+ size_t sendlen = sendminlen, recvlen = 0;
+
time_t t;
struct tm * tm;
+ int frame = 15.;
+ float interval = 1. / frame;
+ int32_t ct = 0;
+ int ch;
+ int ret;
+
+ sendminlen = TIMESTRMAX;
+ UPDATE_TIME();
+
+ memcpy(sendbuf + sendminlen, name, strlen(name));
+ sendminlen += strlen(name);
+ memcpy(sendbuf + sendminlen, "> ", 2);
+ 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
+
+
while (1) {
+ /* update */
+ if (ct % frame == 0) {
+ UPDATE_TIME();
+ }
+ 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);
+ }
/* send */
ch = getch();
- if (ch != -1 && ch != '\n') {
- sendbuf[sendlen++] = ch;
- } else if (ch == '\n') {
- t = time(NULL);
- tm = gmtime(&t);
- strftime(sendbuf - PREFIX_MAX, PREFIX_MAX, "<%Y/%m/%d %H:%M:%S ", tm);
- sendbuf[sendlen++] = '\n';
- send(sockfd, sendbuf - PREFIX_MAX, sendlen + PREFIX_MAX, 0);
- sendlen = 0;
- memset(sendbuf, 0, sendlen);
+ 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);
}
- /* recv */
- recvlen = recv(sockfd, recvbuf, recvmax, MSG_DONTWAIT);
- /* render */
- clear();
- mvaddnstr(0, 0, recvbuf, recvlen);
- mvaddnstr(g_row - 1, 0, sendbuf, sendlen);
- refresh();
+ else if (ch == BACKSPACE) {
+ clearline(g_y - 1);
+ if (sendlen - 1 >= sendminlen)
+ { --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();
}
}