# HG changeset patch # User Matti Hamalainen # Date 1307743848 -10800 # Node ID e7ef3db3b954f294d72b88c4fde52b7496886f5e # Parent eb74097b73f51876da72691d25772f969fcba1c8 Implement "windows" in the chat. Only main window used for now, and even that is not working perfectly yet, tho. diff -r eb74097b73f5 -r e7ef3db3b954 nnchat.c --- a/nnchat.c Sat Jun 11 01:09:27 2011 +0300 +++ b/nnchat.c Sat Jun 11 01:10:48 2011 +0300 @@ -35,8 +35,19 @@ #define SET_BACKBUF_LEN (512) /* Backbuffer size (in lines) */ #define SET_MAX_HISTORY (16) /* Command history length */ #define SET_KEEPALIVE (15*60) /* Ping/keepalive period in seconds */ +#define SET_MAX_WINDOWS (32) +typedef struct { + qringbuf_t *data; /* "Backbuffer" data for this window */ + int pos; /* Current position in the window, 0 = real time */ + char *id; /* Chatter ID, NULL = main window */ + BOOL dirty; + + char *buf; + size_t len, bufsize; +} nn_window_t; + /* Options */ @@ -54,14 +65,17 @@ char optNickSep = ':'; BOOL optDaemon = FALSE; FILE *optLogFile = NULL; -WINDOW *mainWin = NULL, - *statusWin = NULL, - *editWin = NULL; BOOL setPrvMode = FALSE; BOOL setIgnoreMode = FALSE; BOOL optDebug = FALSE; BOOL optLogEnable = FALSE; +nn_window_t *chatWindows[SET_MAX_WINDOWS], + *currWin = NULL; +WINDOW *mainWin = NULL, + *statusWin = NULL, + *editWin = NULL; + qlist_t *setIgnoreList = NULL, *setIdleMessages = NULL; nn_userhash_t *nnUsers = NULL; @@ -174,6 +188,7 @@ return TRUE; } + BOOL getTimeStamp(char *str, size_t len, const char *fmt) { time_t stamp = time(NULL); @@ -188,23 +203,43 @@ } -char *encodeUsername(char *str) +nn_window_t *nn_window_new() { - unsigned char *c = (unsigned char *) str; - if (str == NULL) return NULL; - for (; *c ; c++) - if (*c == ' ') *c = 255; - return str; + nn_window_t *res = th_calloc(1, sizeof(nn_window_t)); + + if (res == NULL) return NULL; + + res->data = th_ringbuf_new(SET_BACKBUF_LEN, th_free); + if (res->data == NULL) { + th_free(res); + return NULL; + } + + return res; } -char *decodeUsername(char *str) +void nn_window_free(nn_window_t *win) { - unsigned char *c = (unsigned char *) str; - if (str == NULL) return NULL; - for (; *c ; c++) - if (*c == 255) *c = ' '; - return str; + if (win != NULL) { + th_ringbuf_free(win->data); + th_free(win->id); + th_free(win); + } +} + + +nn_window_t *nn_find_window(const char *id) +{ + int i; + + for (i = 0; i < SET_MAX_WINDOWS; i++) + if (chatWindows[i] != NULL && + chatWindows[i]->id != NULL && + strcasecmp(id, chatWindows[i]->id) == 0) + return chatWindows[i]; + + return NULL; } @@ -247,13 +282,14 @@ wrefresh(statusWin); } + void printEditBuf(const char *str, nn_editbuf_t *buf) { char *tmp; - if (statusWin == NULL || buf == NULL) return; + if (editWin == NULL || buf == NULL) return; buf->data[buf->len] = 0; - tmp = decodeUsername(th_strdup(buf->data)); + tmp = nn_username_decode(th_strdup(buf->data)); werase(editWin); @@ -307,11 +343,7 @@ } } } else { - if ((unsigned char) *s == 255) - waddch(win, ((unsigned char) ' ') | col); - else - if (*s != '\r') - waddch(win, ((unsigned char) *s) | col); + waddch(win, ((unsigned char) *s) | col); s++; } } @@ -319,6 +351,55 @@ } +void nn_window_print(nn_window_t *win, const char *fmt) +{ + const char *s = fmt; + while (*s) { + if (*s == '\n') { + th_vputch(&(win->buf), &(win->bufsize), &(win->len), '\n'); + th_vputch(&(win->buf), &(win->bufsize), &(win->len), 0); + th_ringbuf_add(win->data, win->buf); + win->buf = NULL; + win->dirty = TRUE; + } + else + if ((unsigned char) *s == 255) + th_vputch(&(win->buf), &(win->bufsize), &(win->len), ' '); + else + if (*s != '\r') + th_vputch(&(win->buf), &(win->bufsize), &(win->len), *s); + s++; + } +} + + +void updateMainWin(BOOL force) +{ + int y, w, h, offs; + qringbuf_t *buf; + + if (mainWin == NULL || currWin == NULL) return; + if (!force && !currWin->dirty) return; + + buf = currWin->data; + getmaxyx(mainWin, h, w); + werase(mainWin); + + offs = buf->size - h - currWin->pos; + if (offs < 0) + offs = 0; + + for (y = 0; y < h && offs < buf->size; offs++) { + if (buf->data[offs] != NULL) + printWin(mainWin, (char *) buf->data[offs]); + y = getcury(mainWin); + } + + currWin->dirty = FALSE; + wrefresh(mainWin); +} + + int printFile(FILE *outFile, const char *fmt) { const char *s = fmt; @@ -346,13 +427,13 @@ return 0; } -void printMsgV(int flags, const char *fmt, va_list ap) +void printMsgV(nn_window_t *win, int flags, const char *fmt, va_list ap) { - char tmpStr[128], buf[8192]; + char tmpStr[128], *buf; getTimeStamp(tmpStr, sizeof(tmpStr), "½17½[½11½%H:%M:%S½17½]½0½ "); - vsnprintf(buf, sizeof(buf), fmt, ap); + buf = th_strdup_vprintf(fmt, ap); if (optLogFile && (flags & LOG_FILE)) { if (flags & LOG_STAMP) printFile(optLogFile, tmpStr); @@ -361,36 +442,39 @@ } if (!optDaemon && (flags & LOG_WINDOW)) { - if (flags & LOG_STAMP) printWin(mainWin, tmpStr); - printWin(mainWin, buf); - wrefresh(mainWin); + nn_window_t *tmp = win != NULL ? win : chatWindows[0]; + if (flags & LOG_STAMP) nn_window_print(tmp, tmpStr); + nn_window_print(tmp, buf); + updateMainWin(FALSE); } + + th_free(buf); } -void printMsg(const char *fmt, ...) +void printMsg(nn_window_t *win, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printMsgV(LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); + printMsgV(win, LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); va_end(ap); } -void printMsgC(const char *fmt, ...) +void printMsgC(nn_window_t *win, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printMsgV(LOG_WINDOW | LOG_FILE, fmt, ap); + printMsgV(win, LOG_WINDOW | LOG_FILE, fmt, ap); va_end(ap); } -void printMsgQ(BOOL logOnly, const char *fmt, ...) +void printMsgQ(nn_window_t *win, BOOL logOnly, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printMsgV(logOnly ? (LOG_STAMP | LOG_FILE) : (LOG_STAMP | LOG_WINDOW | LOG_FILE), fmt, ap); + printMsgV(win, logOnly ? (LOG_STAMP | LOG_FILE) : (LOG_STAMP | LOG_WINDOW | LOG_FILE), fmt, ap); va_end(ap); } @@ -403,7 +487,7 @@ va_list ap2; va_copy(ap2, ap); - printMsgV(LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); + printMsgV(chatWindows[0], LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); tmp = th_strdup_vprintf(fmt, ap2); @@ -434,7 +518,7 @@ void messageFunc(struct _nn_conn_t *conn, const char *fmt, va_list ap) { (void) conn; - printMsgV(LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); + printMsgV(chatWindows[0], LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); } @@ -500,13 +584,13 @@ setTarget = th_strdup(h + 9); for (q = setTarget; *q && *q != ':'; q++); *q = 0; - printMsg("PRV target autoset to '%s'\n", setTarget); + printMsg(NULL, "PRV target autoset to '%s'\n", setTarget); } - printMsgQ(isIgnored, "½11½%s½0½\n", h); + printMsgQ(NULL, isIgnored, "½11½%s½0½\n", h); } else { /* It's an action (/me) */ h = nn_decode_str2(t); - printMsgQ(isIgnored, "½9½* %s½0½\n", h); + printMsgQ(NULL, isIgnored, "½9½* %s½0½\n", h); } th_free(h); th_free(t); @@ -514,7 +598,7 @@ /* It's a normal message */ t = nn_strip_tags(s); h = nn_decode_str2(t); - printMsgQ(isIgnored, "½5½<½%d½%s½5½>½0½ %s\n", isMine ? 14 : 15, userName, h); + printMsgQ(NULL, isIgnored, "½5½<½%d½%s½5½>½0½ %s\n", isMine ? 14 : 15, userName, h); th_free(h); th_free(t); } @@ -533,10 +617,10 @@ getTimeStamp(tmpStr, sizeof(tmpStr), "%c"); if (!strncmp(str, "FAILURE", 7)) { - printMsg("½1½Login failure½0½ - ½3½%s½0½\n", tmpStr); + printMsg(NULL, "½1½Login failure½0½ - ½3½%s½0½\n", tmpStr); return -2; } else if (!strncmp(str, "SUCCESS", 7)) { - printMsg("½2½Login success½0½ - ½3½%s½0½\n", tmpStr); + printMsg(NULL, "½2½Login success½0½ - ½3½%s½0½\n", tmpStr); nn_conn_send_msg(conn, optUserNameEnc, "%%2FRequestUserList"); return 0; } else @@ -556,9 +640,9 @@ p = nn_dbldecode_str(str); if (!p) return -1; - nn_userhash_insert(nnUsers, encodeUsername(p)); + nn_userhash_insert(nnUsers, nn_username_encode(p)); - printMsg("! ½3½%s½0½ ½2½ADDED.½0½\n", p); + printMsg(NULL, "! ½3½%s½0½ ½2½ADDED.½0½\n", p); th_free(p); return 0; } @@ -576,9 +660,9 @@ p = nn_dbldecode_str(str); if (!p) return -1; - nn_userhash_delete(nnUsers, encodeUsername(p)); + nn_userhash_delete(nnUsers, nn_username_encode(p)); - printMsg("! ½3½%s½0½ ½1½DELETED.½0½\n", p); + printMsg(NULL, "! ½3½%s½0½ ½1½DELETED.½0½\n", p); th_free(p); return 0; } @@ -637,7 +721,7 @@ } if (optDebug) { - printMsg("Unknown protocmd: \"%s\"\n", buf); + printMsg(NULL, "Unknown protocmd: \"%s\"\n", buf); return 0; } else return 1; @@ -666,7 +750,7 @@ buf[bufLen--] = 0; /* Decode completed usernames */ - decodeUsername(buf); + nn_username_decode(buf); /* Check for special user commands */ if (*buf == 0) { @@ -676,11 +760,11 @@ /* Change color */ int tmpInt; if ((tmpInt = th_get_hex_triplet(trimLeft(buf + 7))) < 0) { - printMsg("Invalid color value '%s'\n", buf+7); + printMsg(currWin, "Invalid color value '%s'\n", buf+7); return 1; } optUserColor = tmpInt; - printMsg("Setting color to #%06x\n", optUserColor); + printMsg(currWin, "Setting color to #%06x\n", optUserColor); nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); return 0; } @@ -690,26 +774,44 @@ /* Add or remove someone to/from ignore */ qlist_t *user = th_llist_find_func(setIgnoreList, name, compareUsername); if (user != NULL) { - printMsg("Removed user '%s' from ignore.\n", name); + printMsg(currWin, "Removed user '%s' from ignore.\n", name); th_llist_delete_node(&setIgnoreList, user); } else { - printMsg("Now ignoring '%s'.\n", name); + printMsg(currWin, "Now ignoring '%s'.\n", name); th_llist_append(&setIgnoreList, th_strdup(name)); } } else { /* Just list whomever is in ignore now */ qlist_t *user = setIgnoreList; ssize_t nuser = th_llist_length(setIgnoreList); - printMsg("Users ignored (%d): ", nuser); + char *result = th_strdup_printf("Users ignored (%d): ", nuser); while (user != NULL) { if (user->data != NULL) { - printMsgC("'%s'", (char *) user->data); + th_pstr_printf(&result, "%s'%s'", result, (char *) user->data); if (--nuser > 0) - printMsgC(", "); + th_pstr_printf(&result, "%s, ", result); } user = user->next; } - printMsgC("\n"); + printMsg(currWin, "%s\n", result); + th_free(result); + } + return 0; + } + else if (!strncasecmp(buf, "/query", 6)) { + char *name = trimLeft(buf + 6); + if (strlen(name) > 0) { +// qlist_t *user = th_llist_find_func(setIgnoreList, name, compareUsername); + printMsg(currWin, "Opening query for '%s'.\n", name); + + } else { + } + return 0; + } + else if (!strncasecmp(buf, "/win", 4)) { + char *name = trimLeft(buf + 4); + if (strlen(name) > 0) { + } else { } return 0; } @@ -717,11 +819,11 @@ /* Save configuration */ FILE *cfgfile = fopen(setConfigFile, "w"); if (cfgfile == NULL) { - printMsg("Could not create configuration to file '%s': %s\n", setConfigFile, - strerror(errno)); + printMsg(currWin, "Could not create configuration to file '%s': %s\n", + setConfigFile, strerror(errno)); return 0; } - printMsg("Configuration saved in file '%s', res=%d\n", + printMsg(currWin, "Configuration saved in file '%s', res=%d\n", setConfigFile, th_cfg_write(cfgfile, setConfigFile, cfg)); @@ -732,7 +834,7 @@ /* Open given username's profile via firefox in a new tab */ char *name = trimLeft(buf + 3); - printMsg("Opening profile for: '%s'\n", name); + printMsg(currWin, "Opening profile for: '%s'\n", name); tmpStr = nn_encode_str1(name); #ifdef __WIN32 @@ -742,7 +844,7 @@ th_free(tmpStr); status = ShellExecute(NULL, "open", tmpBuf, NULL, NULL, SW_SHOWNA); if (status <= 32) - printMsg("Could not launch default web browser: %d\n", status); + printMsg(currWin, "Could not launch default web browser: %d\n", status); } #else { @@ -754,12 +856,12 @@ if (pipe(fds) == -1) { int ret = errno; - printMsg("Could not open process communication pipe! (%d, %s)\n", ret, strerror(ret)); + printMsg(currWin, "Could not open process communication pipe! (%d, %s)\n", ret, strerror(ret)); return 0; } if ((pid = fork()) < 0) { - printMsg("Could not create sub-process!\n"); + printMsg(currWin, "Could not create sub-process!\n"); } else if (pid == 0) { dup2(fds[1], STDOUT_FILENO); dup2(fds[0], STDERR_FILENO); @@ -778,10 +880,10 @@ th_free(setTarget); if (strlen(name) > 0) { setTarget = th_strdup(trimLeft(buf + 3)); - printMsg("Set prv target to '%s'\n", setTarget); + printMsg(NULL, "Set prv target to '%s'\n", setTarget); } else { setTarget = NULL; - printMsg("Cleared prv target.\n"); + printMsg(NULL, "Cleared prv target.\n"); } return 0; } @@ -796,14 +898,14 @@ snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", setTarget, buf); buf = tmpBuf; } else { - printMsg("No target set, exiting prv mode.\n"); + printMsg(NULL, "No target set, exiting prv mode.\n"); setPrvMode = FALSE; return 1; } } /* Send double-encoded */ - tmpStr = nn_dblencode_str(decodeUsername(buf)); + tmpStr = nn_dblencode_str(nn_username_decode(buf)); if (tmpStr == 0) return -2; result = nn_conn_send_msg(conn, optUserNameEnc, "%s", tmpStr); th_free(tmpStr); @@ -831,7 +933,7 @@ editWin = subwin(stdscr, 3, w, h - 3, 0); if (mainWin == NULL || statusWin == NULL || editWin == NULL) { - THERR("Could not create ncurses windows!\n"); + THERR("Could not create curses chatWindows!\n"); return FALSE; } scrollok(mainWin, 1); @@ -878,7 +980,7 @@ mode = 3; } else { if (optDebug) - printMsg("no mode\n"); + printMsg(currWin, "no mode\n"); return FALSE; } @@ -886,7 +988,7 @@ endPos--; if (startPos > 0) { if (optDebug) - printMsg("str[endPos] == optNickSep && startPos > 0 (%d)\n", startPos); + printMsg(currWin, "str[endPos] == optNickSep && startPos > 0 (%d)\n", startPos); return FALSE; } hasSeparator = TRUE; @@ -913,7 +1015,7 @@ } if (optDebug) { - printMsg("sPos=%d, ePos=%d <-> bPos=%d, bufLen=%d : pat='%s' (again=%s, hassep=%s, hasspc=%s, newpat=%s, mode=%d)\n", + printMsg(currWin, "sPos=%d, ePos=%d <-> bPos=%d, bufLen=%d : pat='%s' (again=%s, hassep=%s, hasspc=%s, newpat=%s, mode=%d)\n", startPos, endPos, buf->pos, buf->len, pattern, again ? "yes" : "no", hasSeparator ? "yes" : "no", @@ -928,7 +1030,7 @@ int i; char *c = user->name; if (optDebug) - printMsg("match='%s' / prev='%s'\n", user->name, previous); + printMsg(currWin, "match='%s' / prev='%s'\n", user->name, previous); for (i = startPos; i <= endPos; i++) nn_editbuf_delete(buf, startPos); @@ -1067,11 +1169,9 @@ nn_editbuf_t *histBuf[SET_MAX_HISTORY+2]; int histPos = 0, histMax = 0; - nn_ringbuf_t *backBuf; - cfgitem_t *tmpcfg; char *homeDir = NULL; - + /* Initialize */ th_init("NNChat", "Newbie Nudes chat client", NN_VERSION, @@ -1208,7 +1308,10 @@ if (!initializeWindows()) goto err_exit; - + + memset(chatWindows, 0, sizeof(chatWindows)); + chatWindows[0] = nn_window_new(); + currWin = chatWindows[0]; updateStatus(insertMode); } @@ -1225,13 +1328,13 @@ } /* Okay ... */ - printMsg("Trying to resolve host '%s' ...\n", optServer); + printMsg(currWin, "Trying to resolve host '%s' ...\n", optServer); tmpHost = gethostbyname(optServer); if (tmpHost == NULL) { errorMsg("Could not resolve hostname: %s.\n", strerror(h_errno)); goto err_exit; } - printMsg("True hostname: %s\n", tmpHost->h_name); + printMsg(currWin, "True hostname: %s\n", tmpHost->h_name); /* To emulate the official client, we first make a request for * policy file, even though we don't use it for anything... @@ -1249,9 +1352,9 @@ } else { int cres = nn_conn_pull(conn); if (cres == 0) { - printMsg("Probe got: %s\n", conn->buf); + printMsg(currWin, "Probe got: %s\n", conn->buf); } else { - printMsg("Could not get policy probe.\n"); + printMsg(currWin, "Could not get policy probe.\n"); } } nn_conn_close(conn); @@ -1279,13 +1382,18 @@ if (cursesInit) { /* Initialize rest of interactive UI code */ memset(histBuf, 0, sizeof(histBuf)); - backBuf = nn_ringbuf_new(SET_BACKBUF_LEN); + memset(histBuf, 0, sizeof(histBuf)); nn_editbuf_clear(editBuf); /* First update of screen */ printEditBuf("", editBuf); updateStatus(insertMode); + + printMsg(NULL, "%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname); + printMsg(NULL, "%s\n", th_prog_author); + printMsg(NULL, "%s\n", th_prog_license); } + /* Enter mainloop */ while (!isError && !exitProg) { @@ -1297,7 +1405,7 @@ if (result > 0) { /* Couldn't handle the message for some reason */ - printMsg("Could not handle: %s\n", conn->ptr); + printMsg(currWin, "Could not handle: %s\n", conn->ptr); } else if (result < 0) { /* Fatal error, quit */ errorMsg("Fatal error with message: %s\n", conn->ptr); @@ -1314,7 +1422,7 @@ /* Handle user input */ if (cursesInit) { int c, cnt = 0; - BOOL update = FALSE; + BOOL update = FALSE, updateMain = TRUE; /* Handle several buffered keypresses at once */ do { @@ -1327,7 +1435,7 @@ case 'd': c = 0x204; break; case 'c': c = 0x206; break; default: - printMsg("Unhandled ESC-O key sequence 0x%02x\n", c); + printMsg(currWin, "Unhandled ESC-O key sequence 0x%02x\n", c); break; } } else @@ -1352,7 +1460,7 @@ case 0x38: c = KEY_END; break; default: - printMsg("Unhandled ESC-[*~ key sequence 0x%02x\n", c); + printMsg(currWin, "Unhandled ESC-[*~ key sequence 0x%02x\n", c); c = ERR; break; } @@ -1360,7 +1468,7 @@ if (c != ERR) wgetch(stdscr); } else { - printMsg("Unhandled ESC key sequence 0x%02x\n", c); + printMsg(currWin, "Unhandled ESC key sequence 0x%02x\n", c); continue; } } @@ -1376,10 +1484,12 @@ #endif if (!initializeWindows()) { - errorMsg("Error resizing curses windows\n"); + errorMsg("Error resizing curses chatWindows\n"); isError = TRUE; } + update = TRUE; + updateMain = TRUE; break; #endif @@ -1488,12 +1598,12 @@ case KEY_F(5): /* F5 = Ignore mode */ setIgnoreMode = !setIgnoreMode; - printMsg("Ignore mode = %s\n", setIgnoreMode ? "ON" : "OFF"); + printMsg(currWin, "Ignore mode = %s\n", setIgnoreMode ? "ON" : "OFF"); break; case KEY_F(7): /* F7 = Clear PRV target */ if (setTarget) { - printMsg("Cleared PRV target.\n"); + printMsg(NULL, "Cleared PRV target.\n"); setPrvMode = FALSE; th_free(setTarget); setTarget = NULL; @@ -1513,7 +1623,7 @@ case 0x03: /* ^C = quit */ case KEY_F(9): /* F9 = Quit */ - printMsg("Quitting per user request.\n"); + printMsg(currWin, "Quitting per user request.\n"); exitProg = TRUE; break; @@ -1524,26 +1634,26 @@ case 0x0c: /* Ctrl + L */ updateWindows(); + update = TRUE; + updateMain = TRUE; break; case KEY_NPAGE: case KEY_PPAGE: -#if 0 + if (currWin != NULL) { - int nlines, ncol, old; - getmaxyx(mainWin, nlines, ncol); - nlines /= 2; - old = backBufPos; + int numLines, numCols, oldPos = currWin->pos; + getmaxyx(mainWin, numLines, numCols); + numLines = (numLines / 2) + 1; - if (c == KEY_NPAGE) - backBufPos = (backBufPos > nlines) ? backBufPos - nlines : 0; + if (c == KEY_PPAGE) + currWin->pos = (currWin->pos > numLines) ? currWin->pos - numLines : 0; else - backBufPos = (backBufPos < ); + currWin->pos = (currWin->pos < currWin->data->n - numLines) ? currWin->pos + numLines : currWin->data->n - numLines; - if (old != backBufPos) - updateMain(); + if (oldPos != currWin->pos) + updateMain = TRUE; } -#endif break; case ERR: @@ -1559,7 +1669,7 @@ nn_editbuf_setpos(editBuf, editBuf->pos + 1); update = TRUE; } else { - printMsg("Unhandled key: 0x%02x\n", c); + printMsg(currWin, "Unhandled key: 0x%02x\n", c); } break; } @@ -1571,6 +1681,8 @@ updateStatus(insertMode); firstUpdate = FALSE; /* a nasty hack ... */ } + + updateMainWin(updateMain); } /* cursesInit */ if (++updateCount > 10) { @@ -1584,9 +1696,6 @@ if (!colorSet) { colorSet = TRUE; - printMsg("%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname); - printMsg("%s\n", th_prog_author); - printMsg("%s\n", th_prog_license); nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); } @@ -1599,19 +1708,25 @@ /* Shutdown */ err_exit: nn_userhash_free(nnUsers); - nn_ringbuf_free(backBuf); nn_editbuf_free(editBuf); - for (histPos = 0; histPos <= SET_MAX_HISTORY; histPos++) - nn_editbuf_free(histBuf[histPos]); - -#ifdef __WIN32 - if (errorMessages || isError) { + + { + int i; + for (i = 0; i <= SET_MAX_HISTORY; i++) + nn_editbuf_free(histBuf[i]); + + for (i = 0; i < SET_MAX_WINDOWS; i++) + nn_window_free(chatWindows[i]); + } + +//#ifdef __WIN32 + if (errorMessages) { char *tmp; wclear(editWin); tmp = promptRequester(editWin, "Press enter to quit.\n", FALSE); th_free(tmp); } -#endif +//#endif if (cursesInit) { if (curVis != ERR)