|
- /* 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.
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <signal.h>
- #include <time.h>
- #include <errno.h>
- #include <ctype.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>
-
- #define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"
- #define PORT "50000"
- #define NAME "anonymous"
-
- #define streq(a,b) (!strcmp(a,b))
- #define strneq(a,b,c) (!memcmp(a,b,c))
-
- int g_sockfd;
-
- #define g_y LINES
- #define g_x COLS
-
- #define HELP \
- "%s [options ...]\n" \
- "\n-serv SERVER Sets the server to connect to [default: " SERV "]" \
- "\n-port PORT Sets the port [default: " PORT "]" \
- "\n-name NAME Sets your display name [default: " NAME "]\n" \
- "\nControls\n" \
- "\nC-l Refreshes the screen" \
- "\nC-w Delete the previous word" \
- "\nC-c Close the client" \
- "\nUp/Down Scrolls all the way up/Scrolls down by a line\n"
-
-
- /* I know, and I don't care */
-
- #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_H 8 /* BACKSPACE */
- #define C_L 12 /* REFRESH */
- #define C_U 21 /* CLR TO BOL */
- #define C_W 23 /* DELETE PREVWORD */
- #define C_L 12 /* Signal full refresh */
-
- /****/
-
- void disconnect(void) {
- int sockfd = g_sockfd;
- if (sockfd > -1) { close(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;
- hints.ai_socktype = SOCK_STREAM;
-
- if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) {
- perror("init_connection");
- 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:
- if (sockfd > -1) { close(sockfd); }
- perror("init_connection");
- exit(1);
- __builtin_unreachable();
- }
-
- //
-
- void fillline(WINDOW * w, int y, int xstart, char c) {
- int i = xstart, x = g_x;
- for (; i < x; ++i)
- { mvwaddch(w,y,i,c); }
- }
-
- void clearline(WINDOW * w, int y) {
- wmove(w, y, 0);
- wclrtoeol(w);
- }
-
- void sanitize(char * buf, size_t rem) {
- char * base = buf;
- buf += rem;
- while (*buf && buf - base) {
- if (*buf < ' ' || *buf > '~') {
- if (*buf != '\n')
- { *buf = '!'; }
- }
- --buf;
- }
- }
-
- int main (int argc, char ** argv) {
- char * serv = SERV, * port = PORT, name[32] = NAME;
- int sockfd;
- {
- char * argv0 = argv[0];
-
- while (++argv, --argc) {
- if (streq(*argv, "-help")) {
- printf(HELP, argv0);
- exit(1);
- }
- if (argc - 1)
- { --argc; ++argv; }
- else {
- printf("%s: %s requires argument\n", argv0, *argv);
- return 1;
- }
- if (streq(*(argv-1), "-serv")) {
- serv = *argv;
- } else if (streq(*(argv-1), "-port")) {
- port = *argv;
- } else if (streq(*(argv-1), "-name")) {
- memset(name, 0, 31);
- strncpy(name, *argv, 31);
- }
- }
-
- printf("Connecting to %s:%s as %s\n", serv, port, name);
-
- atexit(disconnect);
- sockfd = init_connection(serv, port);
- }
-
- initscr();
- noecho();
- keypad(stdscr, ERR);
- nodelay(stdscr, TRUE);
- ESCDELAY = 0;
- curs_set(0);
- if (has_colors() && can_change_color()) {
- short bg, fg;
- /* leaks memory :( */
- start_color();
- for (bg = 0; bg < 8; ++bg) {
- for (fg = 0; fg < 8; ++fg) {
- init_pair(16 + fg + (bg * 8), fg, bg);
- }
- }
- }
- clear();
-
- #define WINCOUNT 3
- WINDOW * w[WINCOUNT];
- #define header w[0]
- #define body w[1]
- #define input w[2]
-
- #define SENDMAX (1 << 8) - 1
- #define RECVMAX 17663 /* (1 << 11) */
-
- char raw[SENDMAX + RECVMAX];
- char * sendbuf = raw, * recvbuf = raw + SENDMAX, * off = recvbuf;
- size_t sendminlen, sendlen, recvlen = 0, offlen = recvlen;
- size_t edit;
-
- #define TIMESTR "<%Y/%m/%d %H:%M:%S "
- #define TIMESTRMAX 21
-
-
- #define UPDATE_TIME()\
- do { \
- t = time(NULL); \
- tm = gmtime(&t); \
- strftime(sendbuf, TIMESTRMAX, TIMESTR, tm); \
- sendbuf[TIMESTRMAX - 1] = ' '; \
- } while (0)
-
- time_t t;
- struct tm * tm;
-
- useconds_t frame = 30;
- useconds_t interval = 1000000. / frame;
- int32_t ct;
-
- int ch, ret;
- size_t i;
-
- size_t namelen = strlen(name);
-
- edit = sendlen = sendminlen = TIMESTRMAX + namelen + 2;
-
- /* fill in the name */
- memcpy(sendbuf + TIMESTRMAX, name, namelen);
- memcpy(sendbuf + TIMESTRMAX + namelen, "> ", 2);
-
- int inputrefresh, bodyrefresh;
-
- hardrefresh:
- ct = 0;
- inputrefresh = bodyrefresh = 1;
-
- header = newwin(1, g_x, 0, 0);
- body = newwin(g_y - 4, g_x, 1, 0);
- input = newwin(3, g_x, g_y - 3, 0);
-
- fillline(header, 0, 0, '-');
- mvwprintw(header, 0, 2, " moontalk ");
- fillline(input, 0, 0, '-');
-
- while (1) {
- /* input */
- while ((ch = getch()) != -1) {
- if (ch == KEY_RESIZE || ch == C_L) {
- for (i = 0; i < WINCOUNT; ++i)
- { delwin(w[i]); }
- endwin();
- erase();
- refresh();
- clear();
- flushinp();
- goto hardrefresh;
- }
- else if ((ch > 31 && ch < 127)) {
- if (sendlen + 1 < SENDMAX)
- {
- memmove(sendbuf + edit + 1, sendbuf + edit, sendlen - edit);
- sendbuf[edit++] = ch; ++sendlen;
- }
- inputrefresh = 1;
- }
- else if (ch == '\n') {
- if (sendlen == sendminlen)
- { continue; }
- if (sendlen + 1 < SENDMAX)
- { sendbuf[sendlen++] = '\n'; }
- if (send(sockfd, sendbuf, sendlen, 0) > 0) {
- memcpy(recvbuf + recvlen, sendbuf, (sendlen + recvlen < RECVMAX) * sendlen);
- recvlen += sendlen;
- offlen += sendlen;
- } else {
- mvwprintw(input, 1, 0, "message failed: %s", strerror(errno));
- }
- bodyrefresh = inputrefresh = 1;
- edit = sendlen = sendminlen;
- }
- else if (ch == BACKSPACE || ch == C_H) {
- inputrefresh = 1;
- if (sendlen - 1 >= sendminlen && edit - 1 >= sendminlen)
- {
- memmove(sendbuf + edit - 1, sendbuf + edit, sendlen - edit);
- --sendlen; --edit;
- }
- inputrefresh = 1;
- }
- else if (ch == KEY_LEFT) {
- if (edit > sendminlen) { --edit; }
- }
- else if (ch == KEY_RIGHT) {
- if (edit < sendlen) { ++edit; }
- }
- else if (ch == KEY_DOWN) {
- mvwprintw(input, 1, 150, "scroll down %ld", offlen);
- while ((size_t)(off - recvbuf) < recvlen && *off != '\n') { ++off; }
- if (*off == '\n') { ++off; }
- wclear(body);
- bodyrefresh = 1;
- }
- else if (ch == KEY_UP) {
- mvwprintw(input, 1, 150, "scroll up %ld", offlen);
- if (off - 2 - recvbuf > 0) { off -= 2; }
- while (off - recvbuf > 0 && *off != '\n') { --off; }
- if (*off == '\n') { ++off; }
- bodyrefresh = 1;
- }
- else if (ch == C_W) {
- i = edit;
- while (i > sendminlen && isspace(sendbuf[i - 1])) { --i; }
- while (i > sendminlen && !isspace(sendbuf[i - 1])) { --i; }
- if (i == edit) { continue; }
- mvwprintw(input, 1, 200, "diff:%ld", sendlen - edit);
- /* memmove(sendbuf + i, sendbuf + edit, sendlen - edit); */
- /* sendlen -= edit; */
- /* edit = i; */
- /* mvwprintw(input, 1, 200, "i:%ld:%ld:sendl:%3ld", */
- /* i - sendminlen, (sendbuf + edit) - (sendbuf + i), sendlen - sendminlen); */
- inputrefresh = 1;
- }
- }
- /* update and rendering */
- if (inputrefresh) {
- clearline(input, 2);
- mvwaddnstr(input, 2, 0, sendbuf, sendlen);
- mvwchgat(input, 2, edit, 1, A_REVERSE, 0, NULL);
- }
-
- if (ct % frame == 0) {
- UPDATE_TIME();
- }
-
- if (ct % frame == 0 || bodyrefresh) {
- ret = recv(sockfd, recvbuf + recvlen, RECVMAX - recvlen, MSG_DONTWAIT);
- if (errno && errno != EAGAIN)
- { mvwaddstr(input, 1, 0, strerror(errno)); }
- if (bodyrefresh) {
- bodyrefresh = 0;
- if (!(ret > 0))
- goto _bodyrefresh;
- }
- if (ret > 0) {
- sanitize(recvbuf + recvlen, ret);
- if (ret + recvlen < RECVMAX)
- {
- recvlen += ret;
- offlen += ret;
- }
- _bodyrefresh:
- mvwprintw(input, 1, 50, "render ct:%d ret:%d", ct, ret);
- mvwaddnstr(body, 0,0, off, offlen);
- }
- }
- refresh();
- for (i = 0; i < WINCOUNT; ++i)
- { wnoutrefresh(w[i]); }
- doupdate();
- usleep(interval);
- ++ct;
- }
-
- endwin();
- }
|