AmendHub

Download:

jcs

/

subtext

/

amendments

/

38

chat: Start on multi-user chat


jcs made amendment 38 over 2 years ago
--- chat.c Thu Dec 23 21:00:16 2021 +++ chat.c Thu Dec 23 21:00:16 2021 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <time.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "ansi.h" +#include "chat.h" +#include "db.h" +#include "session.h" +#include "util.h" + +#define MAX_CHAT_INPUT 100 + +static char chat_tbuf[256]; + +void +chat_broadcast(struct session *s, char *str) +{ + short n; + + for (n = 0; n < nsessions; n++) { + if (!sessions[n]->chatting) + continue; + if (strcmp((char *)s->chatting_with_node, + (char *)sessions[n]->chatting_with_node) != 0) + continue; + + chat_printf_line(sessions[n], 1, "%s", str); + } +} + +size_t +chat_printf_line(struct session *s, short around_bar, char *format, ...) +{ + static char chat_pbuf[256]; + char *pbuf_line; + va_list ap; + size_t len, llen, max_llen; + struct tm *tm; + short i; + + tm = localtime((time_t *)&Time); + len = strftime(chat_pbuf, sizeof(chat_pbuf), "[%H:%M] ", tm); + + va_start(ap, format); + len += vsnprintf(chat_pbuf + len, sizeof(chat_pbuf) - len, format, ap); + va_end(ap); + + pbuf_line = chat_pbuf; + max_llen = s->terminal_cols; + + if (around_bar) + session_output_string(s, ansi(s, ANSI_SAVE_CURSOR, NULL)); + + while (len > 0) { + if (len > max_llen) { + /* chop at last word boundary */ + llen = max_llen; + while (llen >= 0 && pbuf_line[llen] != ' ') + llen--; + if (llen == -1) + /* no words to break on, hard break */ + llen = max_llen; + } else { + llen = len; + } + + if (around_bar) + session_output_string(s, + ansi(s, ANSI_CURSOR_ROW_COL, 1, 1, ANSI_DELETE_LINES_N, 1, + ANSI_CURSOR_ROW_COL, s->terminal_rows - 2, 1, + ANSI_INSERT_LINES_N, 1, NULL)); + + if (pbuf_line != chat_pbuf) { + if (!around_bar) + session_output(s, "\r\n", 2); + session_output(s, " ", 3); + } + session_output(s, pbuf_line, llen); + + pbuf_line += llen; + len -= llen; + } + + if (around_bar) + session_output_string(s, ansi(s, ANSI_RESTORE_SAVED_CURSOR, NULL)); + else + session_output(s, "\r\n", 2); + + return 1; /* XXX */ +} + +void +chat_start(struct session *s, char *with_node) +{ + char *input; + short len, alen; + + s->chatting = 1; + if (with_node[0]) + strlcpy((char *)s->chatting_with_node, with_node, + sizeof(s->chatting_with_node)); + else + s->chatting_with_node[0] = '\0'; + + session_output_string(s, + ansi(s, ANSI_CURSOR_ROW_COL, s->terminal_rows, 1, NULL)); + + chat_printf_line(s, 0, "%sWelcome to Multi-User Chat%s", + ansi(s, ANSI_BOLD, NULL), ansi(s, ANSI_RESET, NULL)); + chat_printf_line(s, 0, "Type %s/help%s for available commands", + ansi(s, ANSI_BOLD, NULL), ansi(s, ANSI_RESET, NULL)); + + snprintf(chat_tbuf, sizeof(chat_tbuf), + " [ %s ] [ Chatting with %s ]", + s->user ? s->user->username : "guest", + s->chatting_with_node[0] ? "..." : "everyone"); + session_printf(s, "%s\r\n", session_bar(s, chat_tbuf, NULL)); + + /* TODO: print other users in chat */ + + snprintf(chat_tbuf, sizeof(chat_tbuf), + "*** %s%s has joined chat", + s->user ? s->user->username : "guest", + s->user && s->user->is_sysop ? " (sysop)" : ""); + chat_broadcast(s, chat_tbuf); + + for (;;) { + session_output(s, "> ", 2); + input = session_field_input(s, s->terminal_cols - 3, 0); + if (!input) + break; + session_printf(s, "\r%s", ansi(s, ANSI_ERASE_LINE, NULL)); + snprintf(chat_tbuf, sizeof(chat_tbuf), "<%s> %s", + s->user ? s->user->username : "guest", input); + free(input); + chat_broadcast(s, chat_tbuf); + } +} --- chat.h Thu Dec 23 14:12:24 2021 +++ chat.h Thu Dec 23 14:12:24 2021 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CHAT_H__ +#define __CHAT_H__ + +#include <stddef.h> +#include "session.h" + +void chat_broadcast(struct session *s, char *str); +size_t chat_printf_line(struct session *s, short around_bar, + char *format, ...); +void chat_start(struct session *s, char *with_node); + +#endif --- session.c Wed Dec 15 15:41:30 2021 +++ session.c Thu Dec 23 15:55:44 2021 @@ -20,6 +20,7 @@ #include <string.h> #include "ansi.h" +#include "chat.h" #include "subtext.h" #include "session.h" #include "user.h" @@ -30,7 +31,6 @@ struct session **sessions = NULL; short nsessions = 0; -char session_tbuf[512]; unsigned short sessions_tally = 0; char sessions_tally_day[9] = { 0 }; @@ -50,7 +50,6 @@ session_create(char *node, char *via, struct node_func return NULL; } - session->state = SESSION_STATE_INIT; session->node_funcs = node_funcs; strlcpy(session->node, node, sizeof(session->node)); strlcpy(session->log.node, node, sizeof(session->log.node)); @@ -78,7 +77,7 @@ session_run(struct uthread *uthread, void *arg) /* until we negotiate otherwise */ s->terminal_cols = 80; s->terminal_rows = 24; - sprintf(s->terminal_type, "xterm-color"); + snprintf(s->terminal_type, sizeof(s->terminal_type), "xterm-color"); if (s->node_funcs->setup) s->node_funcs->setup(s); @@ -93,7 +92,7 @@ session_run(struct uthread *uthread, void *arg) "\r\n", db->config.name, s->node); } - if (s->autologin_username) { + if (s->autologin_username[0]) { s->user = user_find(db, s->autologin_username); if (!s->user) { session_output_string(s, "Failed to find autologin user\r\n"); @@ -130,11 +129,19 @@ session_run(struct uthread *uthread, void *arg) while (!done) { session_output_string(s, "Main Menu> "); +get_another_char: c = session_input_char(s); + if (c == '\r') + goto get_another_char; + session_printf(s, "%c\r\n", c); /* TODO: make letter->command dynamic from a resource */ switch (c) { + case 'c': + /* chat */ + chat_start(s, NULL); + break; case 'g': case 'G': /* goodbye */ @@ -145,8 +152,6 @@ session_run(struct uthread *uthread, void *arg) /* who's online */ session_who(s); break; - case '\r': - break; default: session_output_string(s, "Invalid option\r\n"); break; @@ -194,14 +199,13 @@ session_close(struct session **session) size_t session_printf(struct session *session, const char *format, ...) { + static char session_tbuf[512]; va_list ap; size_t len; - + va_start(ap, format); - len = vsprintf(session_tbuf, format, ap); + len = vsnprintf(session_tbuf, sizeof(session_tbuf), format, ap); va_end(ap); - if (len >= sizeof(session_tbuf)) - panic("sprintf overflow in session_output!"); return session_output(session, session_tbuf, len); } @@ -250,6 +254,10 @@ wait_for_char: while (session->ibuflen == 0) { session->node_funcs->input(session); uthread_yield(); + if (session->ibuflen != 0) + break; + if (session->obuflen != 0) + return 0; } ret = session->ibuf[0]; @@ -260,7 +268,7 @@ wait_for_char: session->ibuflen--; } - if (session->last_input == '\r' && ret == '\n') { + if (session->last_input == '\r' && (ret == '\n' || ret == 0)) { /* we already responded to the \r, just ignore this */ session->last_input = ret; goto wait_for_char; @@ -283,6 +291,12 @@ session_field_input(struct session *session, unsigned for (;;) { c = session_input_char(session); switch (c) { + case 0: /* session_input_char bailed */ + if (session->obuflen > 0) { + session->node_funcs->output(session); + uthread_yield(); + } + break; case 8: /* backspace / ^H */ case 127: /* delete, backspace through telnet */ if (ipos == 0) @@ -302,8 +316,8 @@ session_field_input(struct session *session, unsigned if (redraw) { /* TODO */ } else - /* back one, space, back one (\33 oct = \e) */ - session_output(session, "\33[D \33[D", 7); + session_output_string(session, + ansi(session, ANSI_BACKSPACE)); break; case '\r': @@ -439,7 +453,7 @@ session_bar(struct session *s, char *left_str, char *r } if (sidelen >= sizeof(sides[0])) - panic("session_bar: side %d overflow!", nside); + break; } side[sidelen] = '\0'; --- session.h Wed Dec 15 15:14:32 2021 +++ session.h Thu Dec 23 14:26:18 2021 @@ -21,12 +21,6 @@ #include "uthread.h" -enum session_state { - SESSION_STATE_INIT, - SESSION_STATE_LOGIN, - SESSION_STATE_WELCOME -}; - enum session_input_state { SESSION_INPUT_NONE, SESSION_INPUT_LINE, @@ -57,7 +51,6 @@ struct session { unsigned char ibuf[64]; short obuflen; short ibuflen; - enum session_state state; enum session_input_state input_state; unsigned char last_input; unsigned long last_input_at; @@ -65,9 +58,13 @@ struct session { unsigned short terminal_rows; char terminal_type[20]; unsigned short tspeed; - unsigned char ansi; + unsigned char vt100; + unsigned char vt52; unsigned char color; unsigned char cp437; + unsigned char chatting; + unsigned char chatting_with_node[10]; + unsigned char chat_ring_idx; struct session_log log; char autologin_username[32]; struct user *user; --- telnet.c Wed Dec 15 15:22:52 2021 +++ telnet.c Thu Dec 23 14:52:10 2021 @@ -366,6 +366,9 @@ telnet_input(struct session *session) strlcpy(session->terminal_type, (char *)&node->iac_sb + 2, sizeof(session->terminal_type)); + + /* TODO: compare terminal type to list */ + session->vt100 = 1; break; } node->iac_state = TELNET_IAC_STATE_IDLE; --- util.h Tue Dec 14 14:55:01 2021 +++ util.h Thu Dec 23 15:39:11 2021 @@ -25,11 +25,15 @@ #define SIZE_MAX ULONG_MAX #endif +#define nitems(what) (sizeof((what)) / sizeof((what)[0])) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define BOUND(a, min, max) ((a) > (max) ? (max) : ((a) < (min) ? (min) : (a))) + #define SCROLLBAR_WIDTH 16 #define MAX_TEXTEDIT_SIZE 32767L - -#define nitems(what) (sizeof((what)) / sizeof((what)[0])) #ifndef bool typedef Boolean bool;