From 7dbafe3f0fa465949ef66d800a8cbd0b191c9519 Mon Sep 17 00:00:00 2001 From: erdgeist <> Date: Mon, 27 Feb 2012 00:06:17 +0000 Subject: Complete rewrite of user handling. HEADS UP\! --- Makefile | 9 +- vchat-client.c | 8 +- vchat-commands.c | 31 +-- vchat-config.h | 3 +- vchat-protocol.c | 140 ++++++------ vchat-ui.c | 113 ++-------- vchat-user.c | 660 +++++++++++++++++++++++++------------------------------ vchat-user.h | 40 ++++ vchat.h | 43 +--- 9 files changed, 456 insertions(+), 591 deletions(-) create mode 100644 vchat-user.h diff --git a/Makefile b/Makefile index 81c4fbe..ae11362 100755 --- a/Makefile +++ b/Makefile @@ -6,18 +6,19 @@ # configuration # ############################################## -CFLAGS = -Wall -Os -#CFLAGS = -Wall -g -ggdb +#CFLAGS = -Wall -Os +CFLAGS = -Wall -g -ggdb ## use this line when you've got an readline before 4.(x|2) #CFLAGS += -DOLDREADLINE -CFLAGS += $(OLDREADLINE) +#CFLAGS += $(OLDREADLINE) ## you might need one or more of these: #CFLAGS += -I/usr/local/ssl/include -L/usr/local/ssl/lib #CFLAGS += -I/usr/local/include -L/usr/local/lib #CFLAGS += -I/usr/pkg/include -L/usr/pkg/lib +CFLAGS+=-I../readline-6.2/ ## enable dietlibc #CC = diet cc @@ -29,7 +30,7 @@ CFLAGS += $(OLDREADLINE) ## the install prefix best is /usr/local PREFIX=/usr/local -LIBS = -lreadline -lncurses -lssl -lcrypto +LIBS = ../readline-6.2/libreadline.a -lncurses -lssl -lcrypto OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-ssl.o diff --git a/vchat-client.c b/vchat-client.c index 7324cda..d5960a5 100755 --- a/vchat-client.c +++ b/vchat-client.c @@ -27,7 +27,9 @@ #include #include #include + #include "vchat.h" +#include "vchat-user.h" /* version of this module */ char *vchat_cl_version = "$Id$"; @@ -432,8 +434,8 @@ void usage( char *name) { printf (" -s set server (default \"%s\")\n",getstroption(CF_SERVERHOST)); printf (" -p set port (default %s)\n",getstroption(CF_SERVERPORT)); printf (" -c set channel (default %d)\n",getintoption(CF_CHANNEL)); - if (nick) - printf(" -n set nickname (default \"%s\")\n",nick); + if (own_nick_get()) + printf(" -n set nickname (default \"%s\")\n",own_nick_get()); else puts (" -n set nickname"); printf (" -f set from (default \"%s\")\n",getstroption(CF_FROM)); @@ -468,7 +470,7 @@ main (int argc, char **argv) case 's': setstroption(CF_SERVERHOST,optarg); break; case 'p': setstroption(CF_SERVERPORT,optarg); break; case 'c': setintoption(CF_CHANNEL,strtol(optarg,NULL,10)); break; - case 'n': setstroption(CF_NICK,optarg); break; + case 'n': own_nick_set(optarg); break; case 'f': setstroption(CF_FROM,optarg); break; case 'h': usage(argv[0]); exit(0); break; default : usage(argv[0]); exit(1); diff --git a/vchat-commands.c b/vchat-commands.c index b3c955e..e4f1d99 100755 --- a/vchat-commands.c +++ b/vchat-commands.c @@ -26,6 +26,7 @@ /* local includes */ #include "vchat.h" #include "vchat-help.h" +#include "vchat-user.h" /* version of this module */ char *vchat_cm_version = "$Id$"; @@ -155,14 +156,14 @@ static void doaction( char *tail ) { while( *tail == ' ' ) tail++; - + if( *tail ) { /* send users message to server */ snprintf (tmpstr, TMPSTRSIZE, ".a %s", tail); networkoutput (tmpstr); - + /* show action in channel window */ - snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), nick, tail); + snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), own_nick_get(), tail); writechan (tmpstr); } else { /* missing action */ @@ -174,31 +175,31 @@ doaction( char *tail ) static void privatemessagetx ( char *tail ) { char *mesg; - + /* find nick */ while( *tail==' ') tail++; - + /* find message */ mesg = tail; while ( *mesg && *mesg!=' ') mesg++; /* check for nick && message */ if(*tail && *mesg) { - + /* terminate nick, move to rel start */ *mesg++ = '\0'; - + /* form message and send to server */ snprintf (tmpstr, TMPSTRSIZE, ".m %s %s", tail, mesg); networkoutput (tmpstr); - + /* show message in private window */ snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPRIVMSG), tail, mesg); writepriv (tmpstr, 0); - + /* note we messaged someone */ - ul_msgto(tail); - + ul_private_action(tail); + } else { /* Bump user to fill in missing parts */ msgout( *tail ? " Won't send empty message. ":" Recipient missing. " ); @@ -267,11 +268,11 @@ handleline (char *line) static void output_default(char *line ) { /* prepare for output on display */ - snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), nick, line); - + snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), own_nick_get(), line); + /* send original line to server */ networkoutput (line); - + /* output message to channel window */ writechan (tmpstr); } @@ -282,7 +283,7 @@ command_user(char *tail) { while( *tail == ' ') tail++; if( *tail ) { - char * out = ul_matchuser( tail); + char * out = ul_match_user( tail); if( *out ) { snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_USMATCH), tail, out); } else { diff --git a/vchat-config.h b/vchat-config.h index 2f0aaaf..96fed27 100755 --- a/vchat-config.h +++ b/vchat-config.h @@ -28,7 +28,7 @@ extern unsigned int hscroll; static volatile configoption configoptions[] = { /* config-option type name in file default value value localvar */ - {CF_NICK, CO_STR, "nick", NULL, NULL, { .pstr = &nick } }, + {CF_NICK, CO_STR, "nick", NULL, NULL, { NULL } }, {CF_FROM, CO_STR, "from", "vc-alpha-0.17", NULL, { NULL } }, {CF_SERVERHOST, CO_STR, "host", "localhost", NULL, { NULL } }, {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } }, @@ -53,6 +53,7 @@ static volatile configoption configoptions[] = { {CF_SCROLLBPRIVT,CO_INT, "privscrollt",(char *) 0, (char *)-1, { NULL } }, {CF_SCROLLBACKT, CO_INT, "scrolltime", (char *) 86400, (char *)-1, { NULL } }, {CF_BELLPRIV, CO_INT, "bellonpm", (char *) 0, (char *)-1, { NULL } }, + {CF_CASEFIRST, CO_INT, "casefirst", (char *) 0, (char *)-1, { .pint = &ul_case_first } }, {CF_AUTORECONN, CO_INT, "autoreconn", (char *) 0, (char *)-1, { NULL } }, {CF_NIL, CO_NIL, NULL, NULL, NULL, { NULL } }, }; diff --git a/vchat-protocol.c b/vchat-protocol.c index 1a29b07..55c8b55 100755 --- a/vchat-protocol.c +++ b/vchat-protocol.c @@ -31,6 +31,7 @@ /* local includes */ #include "vchat.h" +#include "vchat-user.h" #include "vchat-ssl.h" /* version of this module */ @@ -208,6 +209,7 @@ pubaction (char *message) nick = strchr (message, ' '); nick[0] = '\0'; nick++; + ul_public_action(nick); action = strchr (nick, ' '); action[0] = '\0'; @@ -228,11 +230,12 @@ pubthoughts (char *message) nick = strchr (message, ' '); nick[0] = '\0'; nick++; + ul_public_action(nick); thoughts = strchr (nick, ' '); thoughts[0] = '\0'; thoughts++; - + snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_PUBTHOUGHT),nick,thoughts); writechan (tmpstr); } @@ -269,7 +272,7 @@ static void topicinfo (char *message) { char *channel = NULL, *topic = NULL; - int tmpchan = 0; + int tmpchan = 0, ownchan = own_channel_get(); /* search start of channel number */ channel = strchr (message, ' '); @@ -283,12 +286,12 @@ topicinfo (char *message) /* convert channel number to integer */ tmpchan = atoi (channel); - if (tmpchan == chan) { + if (tmpchan == ownchan ) { /* show change in topic window */ if (strlen(topic)) - snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), chan, topic); + snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); else - snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), chan); + snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), ownchan ); topicline(NULL); } @@ -308,11 +311,12 @@ static void topicchange (char *message) { char *nick = NULL, *topic = NULL; - int len; + int len, ownchan = own_channel_get(); /* search start of nickname */ nick = strchr (message, ' '); nick++; + ul_public_action(nick); /* search start of message before topic, terminate nick */ topic = strchr (nick, ' '); @@ -330,7 +334,7 @@ topicchange (char *message) topic[len-1] = '\0'; /* show change in topic window */ - snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), chan, topic); + snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), ownchan, topic); topicline(NULL); /* announce change in channel window */ @@ -356,15 +360,14 @@ justloggedin (char *message) str2++; /* if we have a new nick, store it */ - if (!nick || strcasecmp (nick, str1)) - setstroption(CF_NICK,str1); + own_nick_set( str1 ); /* show change in console window */ - snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), nick, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); + snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), str1, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); consoleline (NULL); /* announce login as servermessage */ - snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, str2); + snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), str1, str2); writechan (tmpstr); /* we're not logged in, change status and request nicks */ @@ -380,31 +383,15 @@ justloggedin (char *message) void ownjoin (int channel) { - /* change global channel info */ - chan = channel; networkoutput(".t"); - snprintf(tmpstr, TMPSTRSIZE, ".S %d",chan); + snprintf(tmpstr, TMPSTRSIZE, ".S %d",channel); networkoutput(tmpstr); } -/* this user leaves a channel */ -void -ownleave (int channel) -{ - /* change global channel info */ - chan = 0; -} - /* this user changes his nick */ void ownnickchange (char *newnick) { - /* free old nick, store copy of new nick */ - setstroption(CF_NICK,newnick); - - /* show change in console window */ - snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), nick, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); - consoleline (NULL); } /* parse and handle a nick error message @@ -431,12 +418,13 @@ nickerr (char *message) if (!loggedin) { /* free bogus nick */ - setstroption(CF_NICK,NULL); + own_nick_set(NULL); + /* get new nick via vchat-ui.c */ nickprompt (); /* form login message and send it to server */ - snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", nick, getstroption (CF_FROM), getintoption (CF_CHANNEL)); + snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); networkoutput (tmpstr); } } @@ -455,13 +443,13 @@ login (char *message) { writecf (FS_SERV,&message[2]); /* we don't know our nick? */ - if (!nick) { + if (!own_nick_get() ) { /* find message after nick */ msg = strchr (&message[4], ' '); if (msg) { /* terminate string before message and copy nick */ msg[0] = '\0'; - setstroption(CF_NICK,&message[4]); + own_nick_set(&message[4]); } else { /* no string in servers message (huh?), ask user for nick */ nickprompt (); @@ -469,7 +457,7 @@ login (char *message) { } /* form login message and send it to server */ - snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", nick, getstroption (CF_FROM), getintoption (CF_CHANNEL)); + snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); networkoutput (tmpstr); } @@ -485,11 +473,11 @@ anonlogin (char *message) writecf (FS_SERV,&message[2]); /* we don't know our nick? ask for it! */ - if (!nick) + if (!own_nick_get()) nickprompt (); /* form login message and send it to server */ - snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", nick, getstroption (CF_FROM), getintoption (CF_CHANNEL)); + snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", own_nick_get(), getstroption (CF_FROM), getintoption (CF_CHANNEL)); networkoutput (tmpstr); } @@ -497,49 +485,53 @@ anonlogin (char *message) * format: 119 %s .. * vars: %s nick - a users nick */ static void -receivenicks (char *message) -{ +receivenicks (char *message) { char *str1 = NULL, *str2 = NULL; - int mychan = 0; - void (*ul_myfunc)(char*,int); + int chanflag = -1; /* show message to user */ snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_USONLINE), &message[4]); writechan (tmpstr); /* search for channelnumber */ - str1 = strchr (message, ' '); + if( !(str1 = strchr (message, ' ') ) ) return; str1++; + if (str1[0] == '*') { - if (nicks) return; - ul_myfunc = ul_add; + ul_rebuild_list(); str1++; } else { + int mychan; str2 = str1; str1 = strchr(str2,' '); str1[0] = '\0'; mychan = atoi(str2); - ul_myfunc = ul_moveuser; + if( mychan != own_channel_get() ) + return; + + /* Kick all users from the IN_MY_CHANNEL list */ + own_channel_set( own_channel_get() ); + chanflag = 1; } str1++; /* while user .. */ - while (str1) - { - /* search next user */ - str2 = strchr (str1, ' '); - /* there is another user? terminate this one */ - if (str2) { - str2[0] = '\0'; - str2++; - } + while (str1) { + /* search next user */ + str2 = strchr (str1, ' '); + /* there is another user? terminate this one */ + if (str2) { + str2[0] = '\0'; + str2++; + } - /* add this user via vchat-user.c */ - ul_myfunc (str1,mychan); + /* add this user via vchat-user.c */ + ul_add(str1, chanflag); - /* next user .. */ - str1 = str2; - } + /* next user .. */ + str1 = str2; + } + ul_clean(); } /* parse and handle a login message @@ -581,11 +573,13 @@ usersignoff (char *message) /* search start of message, terminate nick */ msg = strchr (nick, ' '); - msg[0] = '\0'; - msg++; + if( msg ) { + msg[0] = '\0'; + msg++; + } /* delete this user via vchat-user.c */ - ul_del (nick, 0); + ul_del (nick); /* show message to user */ snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNOFF), nick, msg); @@ -620,8 +614,13 @@ userjoin (char *message) /* convert channel to integer */ chan = atoi (channel); + /* is it myself joining */ + if( own_nick_check(nick) ) + own_channel_set(chan); + /* notice channel join via vchat-user.c */ - ul_join (nick, chan); + if( own_channel_get() == chan ) + ul_enter_chan(nick); /* show message to user */ snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_JOIN), nick, msg, chan); @@ -657,7 +656,7 @@ userleave (char *message) chan = atoi (channel); /* notice channel leave via vchat-user.c */ - ul_leave (nick, chan); + ul_leave_chan(nick); /* show message to user */ snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_LEAVE), nick, msg, chan); @@ -689,7 +688,13 @@ usernickchange (char *message) msg++; /* notice nickchange via vchat-user.c */ - ul_nickchange (oldnick, newnick); + ul_rename (oldnick, newnick); + + if( own_nick_check(newnick) ) { + /* show change in console window */ + snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), newnick, getstroption (CF_SERVERHOST), getstroption (CF_SERVERPORT)); + consoleline (NULL); + } /* show message to user */ snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NICKCHANGE), oldnick, newnick, msg); @@ -710,11 +715,13 @@ parsemsg (char *message) str2[0] = '\0'; str2++; if (str2[0] == ' ') str2++; - if (!strncasecmp(nick,str2,strlen(nick))) + if (own_nick_check(str1)) snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBMSG),str1,str2); else snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBMSG),str1,str2); writechan (tmpstr); + + ul_public_action(str1); } else if (message[0] == '[') { @@ -723,10 +730,11 @@ parsemsg (char *message) str2[0] = '\0'; str2++; if (str2[0] == ' ') str2++; - if (!strncasecmp(nick,str2,strlen(nick))) + if (own_nick_check( str1 )) snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBURL),str1,str2); else snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBURL),str1,str2); + ul_public_action(str1); writechan (tmpstr); } /* message starts with '*'? must be private */ @@ -739,7 +747,7 @@ parsemsg (char *message) if (str2[0] == ' ') str2++; snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPRIVMSG),str1,str2); writepriv (tmpstr, 1); - ul_msgfrom(str1); + ul_private_action(str1); } /* message starts with a number? must be a servermessage */ else if ((message[0] >= '0') && (message[0] <= '9')) diff --git a/vchat-ui.c b/vchat-ui.c index 136fdee..bb94653 100755 --- a/vchat-ui.c +++ b/vchat-ui.c @@ -30,9 +30,11 @@ #include #include #include -#include "vchat.h" #include +#include "vchat.h" +#include "vchat-user.h" + /* version of this module */ char *vchat_ui_version = "$Id$"; @@ -667,37 +669,6 @@ shrinkprivwin (void) { } } -/* nick completion callback for readline */ -static char ** -vcccomplete (char *text, int start, int end) -{ - char **matches; - matches = (char **) NULL; - /* are we at start of line, with no characters typed? message completion */ - if (start == 0 && end == 0) - { -#ifdef OLDREADLINE - matches = completion_matches (text, (CPFunction *) ul_mnickcomp); -#else - matches = rl_completion_matches (text, (rl_compentry_func_t *) ul_mnickcomp); -#endif - rl_attempted_completion_over = 1; - } - /* start of line? get matches for channel! */ - else if (start == 0) - { -#ifdef OLDREADLINE - matches = completion_matches (text, (CPFunction *) ul_cnickcomp); -#else - matches = rl_completion_matches (text, (rl_compentry_func_t *) ul_cnickcomp); -#endif - /* no, we want no 'normal' completion if started typing on the beginning - * of the line */ - rl_attempted_completion_over = 1; - } - return (matches); -} - /* clear message window */ void clearpriv () @@ -1057,10 +1028,6 @@ drawwin (WINDOW *win, struct sb_data *sb ) } } -#ifdef OLDREADLINE -typedef int rl_command_func_t __P((int, int)); -#endif - /* initialize curses and display */ void initui (void) @@ -1224,9 +1191,6 @@ initui (void) rl_generic_bind (ISFUNC, "\\M-[5~", (void *)scrollup, keymap); rl_generic_bind (ISFUNC, "\\M-[6~", (void *)scrolldown, keymap); -// rl_bind_keyseq( "\\M-[5~", (rl_command_func_t *)scrollup ); -// rl_bind_keyseq( "\\M-[6~", (rl_command_func_t *)scrolldown ); - /* bind TAB to menu complete from readline */ rl_bind_key ('\t', (rl_command_func_t *) rl_menu_complete); @@ -1235,66 +1199,17 @@ initui (void) /* set up nick completion functions .. */ rl_ignore_completion_duplicates = 0; -#ifdef OLDREADLINE - rl_completion_entry_function = (Function *) ul_nickcomp; - rl_attempted_completion_function = vcccomplete; -#else - rl_completion_entry_function = (rl_compentry_func_t *) ul_nickcomp; - rl_attempted_completion_function = (rl_completion_func_t *) vcccomplete; -#endif + rl_sort_completion_matches = 0; + rl_attempted_completion_function = (rl_completion_func_t *) ul_complete_user; /* .. and 'line completed' callback */ -#ifdef OLDREADLINE - rl_callback_handler_install ("", linecomplete); -#else rl_callback_handler_install ("", (rl_vcpfunc_t *) linecomplete); -#endif - if( getintoption(CF_PRIVCOLLAPS) ) toggleprivwin(); -/* - writeout( ">> Ctrl-X <<"); - - if (errstr[0] != '\0') { - writeout(errstr); - writeout( " "); - } - - writeout (vchat_cl_version); - writeout (vchat_ui_version); - writeout (vchat_io_version); - writeout (vchat_us_version); - writeout (vchat_cm_version); - showout( ); -*/ - - resize(0); -} - -/* render colorized line to window */ -/* DOES NOT WRAP !!! - CURRENTLY UNUSED - Enable, when needed - -static void -writecolorized( WINDOW *win, char *string) { - ncurs_attr old_att, new_att; - int i; - - WATTR_GET( win, old_att ); - new_att = old_att; - for( i = 0; string[ i ]; i++ ) - if( string[ i ] == 1 ) { - docolorize( string[++i], &new_att, old_att); - } else { - WATTR_SET( win, new_att ); - waddch( win, string[ i ] ); - } - WATTR_SET( win, old_att ); + resize(0); } -*/ /* render consoleline to screen */ void @@ -1403,18 +1318,18 @@ exitui (void) void nickprompt (void) { - if (nick) + char * newnick = 0; + + if (own_nick_get()) return; /* prompt user for nick unless he enters one */ consoleline("Please enter your nickname:"); - while (!nick || !nick[0]) - { - if (nick) - free (nick); - nick = readline(""); - } - setstroption(CF_NICK,nick); + while (!newnick) + newnick = readline(""); + + own_nick_set(newnick); + setstroption(CF_NICK,newnick); /* try to get readlines stats clean again */ //rl_free_line_state (); diff --git a/vchat-user.c b/vchat-user.c index 4b44080..cd0df14 100755 --- a/vchat-user.c +++ b/vchat-user.c @@ -1,426 +1,364 @@ /* - * vchat-client - alpha version - * vchat-user.c - functions working with the userlist - * - * Copyright (C) 2001 Andreas Kotes - * - * This program is free software. It can be redistributed and/or modified, - * provided that this copyright notice is kept intact. This program is - * distributed in the hope that it will be useful, but without any warranty; - * without even the implied warranty of merchantability or fitness for a - * particular purpose. In no event shall the copyright holder be liable for - * any direct, indirect, incidental or special damages arising in any way out - * of the use of this software. - * - */ - -/* general includes */ + * vchat-client + +*/ + +#include #include +#include #include -#include -#include +#include #include -#include #include -#include "vchat.h" -struct user -{ - char *nick; /* nick of user */ - int chan; /* channel user is on */ - int chan_valid; /* are we sure he is? */ - int client_pv; /* client protocol version */ - int messaged; /* did we message with this user? */ - struct user *next;/* next user in linked list */ -}; +#include "vchat.h" +#include "vchat-user.h" /* version of this module */ char *vchat_us_version = "$Id$"; -/* externally used variables */ -/* current nick */ -char *nick = NULL; -/* current channel */ -int chan = 0; -/* userlist */ -user *nicks = NULL; - -/* add user to userlist */ -void -ul_add (char *name, int ignored) -{ - user *tmp = NULL; - - /* no list? create one */ - if (!nicks) - { - nicks = malloc (sizeof (user)); - memset(nicks,0,sizeof(user)); - nicks->nick = strdup(name); - nicks->chan = 0; /* users default in channel 0 */ - nicks->chan_valid = 0; - nicks->next = NULL; - } - else - { - /* travel list until end */ - tmp = nicks; - while (tmp) - { - /* it is this user? return */ - if (!strcmp (name, tmp->nick)) - return; - /* is there a next user? */ - if (tmp->next) - tmp = tmp->next; - else - { - /* create one */ - tmp->next = malloc (sizeof (user)); - tmp = tmp->next; - memset(tmp,0,sizeof(user)); - tmp->nick = strdup(name); - tmp->chan = 0; - tmp->chan_valid = 0; - tmp->next = NULL; - tmp = NULL; - } - } - } -#ifndef OLDREADLINE - rl_last_func = NULL; -#endif -} - -/* delete user from userlist */ -void -ul_del (char *name, int ignored) +typedef struct { - user *tmp = NULL, *ltmp = NULL; - - /* is it this client? return */ - if (nick && !strcmp (nick, name)) - return; - /* no list? return */ - if (!nicks) - return; - /* the user on top of list? */ - if (!strcmp (name, nicks->nick)) - { - /* remove user and copy next in list */ - tmp = nicks->next; - free (nicks); - nicks = tmp; - return; - } - /* travel through list, skip first entry */ - ltmp = nicks; - tmp = nicks->next; - while (tmp) - { - /* is it this user? */ - if (!strcmp (name, tmp->nick)) - { - /* hook next to last, discard this */ - ltmp->next = tmp->next; - free (tmp); - return; - } - /* advance in list */ - ltmp = tmp; - tmp = tmp->next; - } -#ifndef OLDREADLINE - rl_last_func = NULL; -#endif + char *nick; + enum { UL_NONE = 0x00, UL_ME = 0x01, UL_IN_MY_CHAN = 0x02, UL_NOT_IN_LIST = 0x04 } flags; + uint64_t last_public; + uint64_t last_private; +} user; +static user *g_users; //< all users, incl self +static size_t g_users_count; //< number of users in list +static char *g_nick; //< own nick +static int g_channel; //< own channel +int ul_case_first = 0; + +static int ul_nick_lookup( const char *nick, int *exact_match ) { + int i; + + *exact_match = 1; + for( i=0; inick) || !strcmp(tmpstr, tmp->nick)) - { - return tmp; - } - /* advance in list */ - tmp = tmp->next; +/* own nick and channel setters/getters */ +void own_nick_set( char *nick ) { + if( nick ) { + int base; + if( g_nick ) + base = ul_rename( g_nick, nick ); + else + base = ul_add( nick, 0 ); + if( base >= 0 ) { + g_users[base].flags |= UL_ME; + g_nick = g_users[base].nick; } - return NULL; -} + } else + ul_del( g_nick ); -char * -ul_matchuser( char *regex) { - user *tmp = nicks; - char *dest = tmpstr; - regex_t preg; + setstroption(CF_NICK, nick); +} - *dest = 0; - if( !regcomp( &preg, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { - while( tmp ) { - /* does the username match? */ - if( !regexec( &preg, tmp->nick, 0, NULL, 0)) /* append username to list */ - dest += snprintf ( dest, 256, " %s", tmp->nick); - tmp = tmp->next; - } +void own_channel_set( int channel ) { + if( channel != g_channel ) { + /* Remove all users from my chan, will be re-set on join message */ + int i; + for( i=0; inext && ( tmp->next != who ) ) - tmp = tmp->next; +int own_nick_check( char *nick ) { + if( !g_nick ) return -1; + return !strncasecmp(g_nick,nick,strlen(g_nick) ); +} - if( tmp->next == who ) { - tmp->next = tmp->next->next; - who->next = nicks; - nicks = who; - } -#ifndef OLDREADLINE - rl_last_func = NULL; -#endif +int own_channel_get( ) { + return g_channel; } -void -ul_msgto (char *name) { - user *tmp = ul_finduser(name); +/* Add/remove/rename */ +int ul_add(char *name, int in_my_chan_flag ) { + + /* Test if user is already known */ + int exact_match, base = ul_nick_lookup( name, &exact_match ); + if( !exact_match ) { + /* Make space for new user */ + user * new_users = realloc( g_users, sizeof( user ) * ( 1 + g_users_count ) ); + if( !new_users ) return -1; + + /* Copy the tail */ + g_users = new_users; + memmove( g_users + base + 1, g_users + base, ( g_users_count - base ) * sizeof( user ) ); + g_users[base].nick = strdup( name ); + g_users[base].flags = UL_NONE; + g_users[base].last_public = 0; + g_users[base].last_private = 0; + + g_users_count++; + } - if (tmp) { - tmp->messaged |= 1; - ul_usertofront( tmp ); + g_users[base].flags &= ~UL_NOT_IN_LIST; + switch( in_my_chan_flag ) { + case 1: g_users[base].flags |= UL_IN_MY_CHAN; break; + case 0: g_users[base].flags &= ~UL_IN_MY_CHAN; break; + case -1: default: break; } + + return base; } -void -ul_msgfrom (char *name) { - user *tmp = ul_finduser(name); +int ul_del(char *name) { + /* Test if user is already known */ + int exact_match, base = ul_nick_lookup( name, &exact_match ); + if( !exact_match ) return -1; - if (tmp) { - tmp->messaged |= 2; - ul_usertofront( tmp ); - } -} + /* Release the name buffer */ + free( g_users[base].nick ); + if( g_users[base].flags & UL_ME ) g_nick = 0; -/* set channel of user */ -void -ul_moveuser (char *name, int channel) { - user *tmp = ul_finduser(name); + /* Copy the tail */ + memmove( g_users + base, g_users + base + 1, ( g_users_count - base - 1 ) * sizeof( user ) ); - if (tmp) { - /* store channel information and mark it valid */ - tmp->chan = channel; - tmp->chan_valid = 1; - } -#ifndef OLDREADLINE - rl_last_func = NULL; -#endif + /* Shrink user list, realloc to a smaller size never fails */ + g_users = realloc( g_users, sizeof( user ) * --g_users_count ); + return 0; } -/* let user leave a channel */ -void -ul_leave (char *name, int channel) -{ - user *tmp = ul_finduser(name); - /* is it this client? handle and return */ - if (nick && !strcmp (nick, name)) - { - ownleave (channel); - return; - } +int ul_rename(char *oldname, char *newname) { + /* Ensure user */ + int base = ul_add( oldname, -1 ); + if( base >= 0 ) { + free( g_users[base].nick ); + g_users[base].nick = strdup( newname ); + if( g_users[base].flags & UL_ME ) + g_nick = g_users[base].nick; + if( g_users[base].flags & UL_IN_MY_CHAN ) + ul_public_action(newname); + } + return base; +} - if (tmp) - { - /* mark channel information invalid */ - tmp->chan_valid = 0; - return; - } -#ifndef OLDREADLINE - rl_last_func = NULL; -#endif +void ul_clear() { + int i; + for( i=0; inick); - tmp->nick = strdup (newnick); - return; - } -#ifndef OLDREADLINE - rl_last_func = NULL; -#endif +void ul_rebuild_list( ) { + int i; + for( i=0; inext; - free (tmp->nick); - free (tmp); - /* advance */ - tmp = tmp2; +void ul_clean() { + int i; + for( i=0; inick, text, len)); +/* Seting state */ +void ul_leave_chan(char *name) { + /* Ensure user and kick him off the channel */ + ul_add(name, 0); } -int ulnc_ncasenick(user *tmp, const char *text, int len, int value) { - return (!strncasecmp(tmp->nick, text, len)); +void ul_enter_chan(char *name) { + /* Ensure user and put him on the channel */ + int base = ul_add(name, 1); + if( base >= 0 ) + ul_public_action(name); + + /* Reflect in UI */ + if( own_nick_check( name ) ) + ownjoin( g_channel ); } -char * -ulnc_complete (const char *text, int state, int value, int (*checkfn)(user *,const char *,int,int)) { - static int len; - static user *tmp; - char *name; - - /* first round? reset pointers! */ - if (!state) - { - tmp = nicks; - len = strlen (text); - } +void ul_private_action(char *name) { + /* Ensure user and keep channel state */ + int base = ul_add(name, -1); + if( base >= 0 ) + g_users[base].last_private = ul_now(); +} - /* walk list .. */ - while (tmp) - { - /* we found a match? */ - if (checkfn(tmp,text,len,value)) - { - /* copy nick, advance pointer for next call, return nick */ - name = tmp->nick; - tmp = tmp->next; - return name; - } - else - { - tmp = tmp->next; - } - } - return NULL; +void ul_public_action(char *name) { + /* Ensure user and put him on the channel */ + int base = ul_add(name, 1); + if( base >= 0 ) + g_users[base].last_public = ul_now(); } -/* nick completion functions for readline in vchat-ui.c */ -char * -ul_nickcomp (const char *text, int state) -{ - int ncasemode = 1; - char *name = NULL; - if (!state) ncasemode = 0; - if (!ncasemode) { - name = ulnc_complete(text,state,0,ulnc_casenick); - if (!state && !name) ncasemode = 1; +/* Finding users ul_finduser? */ +char * ul_match_user(char *regex) { + char *dest = tmpstr; + int i; + regex_t preg; + + *dest = 0; + if( !regcomp( &preg, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { + + /* does the username match? */ + /* XXX overflow for too many matches */ + for( i=0; inick, text, len) && (tmp->chan_valid) && (tmp->chan == value)); +static int ul_compare_private( const void *a, const void *b ) { + const user *_a = (const user *)a, *_b = (const user *)b; + if( _a->last_private > _b->last_private ) return -1; + return 1; } -int ulnc_ncasenickc(user *tmp, const char *text, int len, int value) { - return (!strncasecmp(tmp->nick, text, len) && (tmp->chan_valid) && (tmp->chan == value)); -} +static int ul_compare_begin_of_line_ncase( const void *a, const void *b ) { + const user *_a = (const user *)a, *_b = (const user *)b; + size_t tmpstr_len; + int a_i, b_i; -/* nick completion for channel, used by vchat-ui.c */ -char * -ul_cnickcomp (const char *text, int state) -{ - int ncasemode = 1; - static char *name = NULL; + /* First ensure that users in current channel win */ + if( !(_a->flags & UL_IN_MY_CHAN ) ) return 1; + if( !(_b->flags & UL_IN_MY_CHAN ) ) return -1; - if (!state) ncasemode = 0; - if (!ncasemode) { - name = ulnc_complete(text,state,chan,ulnc_casenickc); - if (!state && !name) ncasemode = 1; - } - if (ncasemode) - name = ulnc_complete(text,state,chan,ulnc_ncasenickc); - if (name) { - snprintf(tmpstr,TMPSTRSIZE,"%s:",name); - return strdup(tmpstr); - } else - return NULL; + tmpstr_len = strlen( tmpstr ); + a_i = strncasecmp( _a->nick, tmpstr, tmpstr_len ); + b_i = strncasecmp( _b->nick, tmpstr, tmpstr_len ); + + if( a_i && b_i ) return 0; // Both nicks dont match + if( !a_i && b_i ) return -1; // a matches insensitive, b doesnt + if( a_i && !b_i ) return 1; // b matches insensitive, a doesnt + + /* From here both nicks match the prefix, ensure that own_nick + always appears last */ + if( _a->flags & UL_ME ) return 1; + if( _b->flags & UL_ME ) return -1; + + /* Now the user with the most recent public activity wins */ + if( _a->last_public > _b->last_public ) return -1; + + return 1; } -int ulnc_casenickm(user *tmp, const char *text, int len, int value) { - return (!strncmp(tmp->nick, text, len) && (tmp->messaged)); +static int ul_compare_begin_of_line_case( const void *a, const void *b ) { + const user *_a = (const user *)a, *_b = (const user *)b; + size_t tmpstr_len; + int a_i, b_i, a_s, b_s; + + /* First ensure that users in current channel win */ + if( !(_a->flags & UL_IN_MY_CHAN ) ) return 1; + if( !(_b->flags & UL_IN_MY_CHAN ) ) return -1; + + tmpstr_len = strlen( tmpstr ); + a_i = strncasecmp( _a->nick, tmpstr, tmpstr_len ); + a_s = strncmp ( _a->nick, tmpstr, tmpstr_len ); + b_i = strncasecmp( _b->nick, tmpstr, tmpstr_len ); + b_s = strncmp ( _b->nick, tmpstr, tmpstr_len ); + + if( a_i && b_i ) return 0; // Both nicks dont match at all + if( !a_i && b_i ) return -1; // a matches insensitive, b doesnt + if( a_i && !b_i ) return 1; // b matches insensitive, a doesnt + + if( !a_s && b_s ) return -1; // a matches sensitive, b doesnt + if( a_s && !b_s ) return 1; // b matches sensitive, a doesnt + + /* From now we know that both match with same quality, ensure + that own nick always appears last */ + if( _a->flags & UL_ME ) return 1; + if( _b->flags & UL_ME ) return -1; + + /* Now the user with the most recent public activity wins */ + if( _a->last_public > _b->last_public ) return -1; + + return 1; } -int ulnc_ncasenickm(user *tmp, const char *text, int len, int value) { - return (!strncasecmp(tmp->nick, text, len) && (tmp->messaged)); +static int ul_compare_middle( const void *a, const void *b ) { + const user *_a = (const user *)a, *_b = (const user *)b; + return strcasecmp( _b->nick, _a->nick ); } -/* nick completion for channel, used by vchat-ui.c */ -char * -ul_mnickcomp (const char *text, int state) -{ - int ncasemode = 1; - static char *name = NULL; +/* Nick completion function for readline */ +char **ul_complete_user(char *text, int start, int end ) { + char **result = 0; + int i, result_count = 0; + + /* Never want readline to complete filenames */ + rl_attempted_completion_over = 1; + + /* Prepare return array ... of max g_users_count (char*) + Plus least common prefix in [0] and null terminator + */ + result = malloc( sizeof(char*) * ( 2 + g_users_count ) ); + if( !result ) return 0; + + if( start == 0 && end == 0 ) { + /* Completion on begin of line yields list of everyone we + were in private conversation, sorted by time of last .m */ + qsort( g_users, g_users_count, sizeof(user), ul_compare_private ); + for( i=0; i 0 ) { + /* Completion on begin of line with some chars already typed yields + a list of everyone in channel, matching prefix, sorted by last + public activity */ + snprintf( tmpstr, end + 1, "%s", text ); + if( ul_case_first ) + qsort( g_users, g_users_count, sizeof(user), ul_compare_begin_of_line_case ); + else + qsort( g_users, g_users_count, sizeof(user), ul_compare_begin_of_line_ncase ); - if (!state) ncasemode = 0; - if (!ncasemode) { - name = ulnc_complete(text,state,chan,ulnc_casenickm); - if (!state && !name) ncasemode = 1; - } - if (ncasemode) - name = ulnc_complete(text,state,chan,ulnc_ncasenickm); - if (name) { - snprintf(tmpstr,TMPSTRSIZE,".m %s",name); - return strdup(tmpstr); + for( i=0; i + and thus should complete all users, sorted alphabetically without + preferences. */ + snprintf( tmpstr, end - start + 1, "%s", text ); + qsort( g_users, g_users_count, sizeof(user), ul_compare_middle ); + for( i=0; i + * License: Beerware +*/ +#ifndef __VCHAT_USER_H__ +#define __VCHAT_USER_H__ + +extern char *vchat_us_version; + +/* own nick and channel */ +void own_channel_set( int channel ); +int own_channel_get( ); +void own_nick_set( char *nick ); +char const *own_nick_get( ); +int own_nick_check( char * nick ); + +/* Add/remove/rename */ +int ul_add(char *name, int chan_flag); /* -1: keep, 0: notinchan, 1: inchan */ +int ul_del(char *name); +int ul_rename(char *oldname, char *newname); +void ul_clear(); +void ul_rebuild_list(); +void ul_clean(); + +/* Seting state */ +void ul_leave_chan(char *name); +void ul_enter_chan(char *name); +void ul_private_action(char *name); +void ul_public_action(char *name); + +/* Finding users ul_finduser */ +char *ul_match_user(char *regex); + +/* Nick completion function for readline */ +char **ul_complete_user(char *text, int start, int end ); + +#endif diff --git a/vchat.h b/vchat.h index 1b29155..541fd21 100755 --- a/vchat.h +++ b/vchat.h @@ -14,12 +14,6 @@ * */ -/* user structure */ -struct user; -typedef struct user user; -/* userlist from vchat-user.c */ -extern user *nicks; - /* servermessage types */ typedef enum { SM_IGNORE, SM_INFO, SM_USERINFO, SM_CHANNEL, SM_ERROR } smtype; @@ -39,7 +33,7 @@ typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT, CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, CF_LOGINSCRIPT, CF_USESSL, CF_IGNSSL, CF_USECERT, CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, CF_USETOPIC, CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, -CF_ENCODING, CF_BELLPRIV, CF_AUTORECONN } confopt; +CF_ENCODING, CF_BELLPRIV, CF_CASEFIRST, CF_AUTORECONN } confopt; /* format strings */ typedef enum { FS_PLAIN, FS_CHAN, FS_PRIV, FS_SERV, FS_GLOB, FS_DBG, FS_ERR, @@ -80,9 +74,6 @@ typedef struct formatstring formatstring; #define TMPSTRSIZE 1024 static char tmpstr[TMPSTRSIZE]; -extern char *nick; -extern int chan; - extern unsigned int loggedin; /* vchat-client.c */ @@ -100,38 +91,6 @@ void setstroption (confopt option, char *string); int getintoption (confopt option); void setintoption (confopt option, int value); -/* vchat-user.c */ -extern char *vchat_us_version; - -/* add / delete user */ -void ul_add (char *nick, int ignored); -void ul_del (char *nick, int ignored); - -/* clear userlist */ -void ul_clear (); - -/* channel join / leave */ -void ul_join (char *nick, int channel); -void ul_leave (char *nick, int channel); - -/* nickchange */ -void ul_nickchange (char *oldnick, char *newnick); - -/* place user in channel */ -void ul_moveuser (char *nick, int channel); - -/* message nick completion */ -void ul_msgto (char *nick); -void ul_msgfrom (char *nick); - -/* nick-completion for vchat-ui.c */ -char *ul_nickcomp (const char *text, int state); -char *ul_cnickcomp (const char *text, int state); -char *ul_mnickcomp (const char *text, int state); - -/* try to find user by substring */ -char *ul_matchuser (char *substr); - /* vchat-ui.c */ extern char *vchat_ui_version; -- cgit v1.2.3