|
- /* 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 <string.h>
- #include <unistd.h>
- #include <signal.h>
- #include <time.h>
-
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <arpa/inet.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_y, g_x;
- int g_sockfd = -1;
-
- void init_screen(int x) {
- (void)x;
- signal(SIGWINCH, SIG_IGN);
-
- 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);
- clear();
-
- signal(SIGWINCH, init_screen);
- }
-
- void free_connection(void) {
- close(g_sockfd);
- g_sockfd = -1;
- }
-
- /* always returns an accessible socket */
- int init_connection(char * serv, char * port) {
- int status, sockfd;
- 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
-
- if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) {
- fprintf(stderr, "init_connection: %s\n", gai_strerror(status));
- exit(1);
- }
-
- if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1)
- { goto error; }
- if (connect(sockfd, res->ai_addr, res->ai_addrlen))
- { goto error; }
-
- freeaddrinfo(res);
-
- g_sockfd = sockfd;
- return sockfd;
- error:
- perror("init_connection");
- exit(1);
- __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[32] = "anonymous";
-
- int sockfd;
-
- while (++argv, --argc) {
- if (streq(*argv, "-help")) {
- printf("%s: HELP\n", argv0);
- return 1;
- }
- if (argc - 1)
- { --argc; ++argv; }
- else {
- printf("%s: %s requires argument\n", argv0, *argv);
- 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);
- 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);
-
- 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);
- 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; }
- }
- 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();
- }
- }
|