diff options
Diffstat (limited to 'vchat-user.c')
| -rwxr-xr-x | vchat-user.c | 399 |
1 files changed, 223 insertions, 176 deletions
diff --git a/vchat-user.c b/vchat-user.c index 881a3cf..1ab2048 100755 --- a/vchat-user.c +++ b/vchat-user.c | |||
| @@ -3,42 +3,48 @@ | |||
| 3 | 3 | ||
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | #include <regex.h> | ||
| 6 | #include <stdint.h> | 7 | #include <stdint.h> |
| 8 | #include <stdio.h> | ||
| 7 | #include <stdlib.h> | 9 | #include <stdlib.h> |
| 8 | #include <strings.h> | 10 | #include <strings.h> |
| 9 | #include <stdio.h> | ||
| 10 | #include <sys/time.h> | 11 | #include <sys/time.h> |
| 11 | #include <regex.h> | 12 | |
| 12 | #include <readline/readline.h> | 13 | #include <readline/readline.h> |
| 13 | 14 | ||
| 14 | #include "vchat.h" | ||
| 15 | #include "vchat-user.h" | 15 | #include "vchat-user.h" |
| 16 | #include "vchat.h" | ||
| 16 | 17 | ||
| 17 | /* version of this module */ | 18 | /* version of this module */ |
| 18 | char *vchat_us_version = "vchat-user.c $Id$"; | 19 | char *vchat_us_version = |
| 19 | 20 | "vchat-user.c $Id$"; | |
| 20 | typedef struct | 21 | |
| 21 | { | 22 | typedef struct { |
| 22 | char *nick; | 23 | char *nick; |
| 23 | enum { UL_NONE = 0x00, UL_ME = 0x01, UL_IN_MY_CHAN = 0x02, UL_NOT_IN_LIST = 0x04 } flags; | 24 | enum { |
| 25 | UL_NONE = 0x00, | ||
| 26 | UL_ME = 0x01, | ||
| 27 | UL_IN_MY_CHAN = 0x02, | ||
| 28 | UL_NOT_IN_LIST = 0x04 | ||
| 29 | } flags; | ||
| 24 | uint64_t last_public; | 30 | uint64_t last_public; |
| 25 | uint64_t last_private; | 31 | uint64_t last_private; |
| 26 | } user; | 32 | } user; |
| 27 | static user *g_users; //< all users, incl self | 33 | static user *g_users; //< all users, incl self |
| 28 | static size_t g_users_count; //< number of users in list | 34 | static size_t g_users_count; //< number of users in list |
| 29 | static char *g_nick; //< own nick | 35 | static char *g_nick; //< own nick |
| 30 | static int g_channel; //< own channel | 36 | static int g_channel; //< own channel |
| 31 | unsigned int ul_case_first = 0; | 37 | unsigned int ul_case_first = 0; |
| 32 | 38 | ||
| 33 | static char **g_dict; | 39 | static char **g_dict; |
| 34 | static size_t g_dict_len; | 40 | static size_t g_dict_len; |
| 35 | 41 | ||
| 36 | static int ul_nick_lookup( const char *nick, int *exact_match ) { | 42 | static int ul_nick_lookup(const char *nick, int *exact_match) { |
| 37 | int i; | 43 | int i; |
| 38 | 44 | ||
| 39 | *exact_match = 1; | 45 | *exact_match = 1; |
| 40 | for( i=0; i<g_users_count; ++i ) | 46 | for (i = 0; i < g_users_count; ++i) |
| 41 | if( !strcasecmp( g_users[i].nick, nick ) ) | 47 | if (!strcasecmp(g_users[i].nick, nick)) |
| 42 | return i; | 48 | return i; |
| 43 | *exact_match = 0; | 49 | *exact_match = 0; |
| 44 | return i; | 50 | return i; |
| @@ -46,78 +52,83 @@ static int ul_nick_lookup( const char *nick, int *exact_match ) { | |||
| 46 | 52 | ||
| 47 | static int64_t ul_now() { | 53 | static int64_t ul_now() { |
| 48 | struct timeval now; | 54 | struct timeval now; |
| 49 | gettimeofday(&now,(struct timezone*) 0); | 55 | gettimeofday(&now, (struct timezone *)0); |
| 50 | return ((uint64_t)now.tv_sec * 1000) + ((uint64_t)now.tv_usec / 1000 ); | 56 | return ((uint64_t)now.tv_sec * 1000) + ((uint64_t)now.tv_usec / 1000); |
| 51 | } | 57 | } |
| 52 | 58 | ||
| 53 | /* own nick and channel setters/getters */ | 59 | /* own nick and channel setters/getters */ |
| 54 | void own_nick_set( char *nick ) { | 60 | void own_nick_set(char *nick) { |
| 55 | if( nick ) { | 61 | if (nick) { |
| 56 | int base; | 62 | int base; |
| 57 | if( g_nick ) | 63 | if (g_nick) |
| 58 | base = ul_rename( g_nick, nick ); | 64 | base = ul_rename(g_nick, nick); |
| 59 | else | 65 | else |
| 60 | base = ul_add( nick, 0 ); | 66 | base = ul_add(nick, 0); |
| 61 | if( base >= 0 ) { | 67 | if (base >= 0) { |
| 62 | g_users[base].flags |= UL_ME; | 68 | g_users[base].flags |= UL_ME; |
| 63 | g_nick = g_users[base].nick; | 69 | g_nick = g_users[base].nick; |
| 64 | } | 70 | } |
| 65 | } else | 71 | } else |
| 66 | ul_del( g_nick ); | 72 | ul_del(g_nick); |
| 67 | 73 | ||
| 68 | setstroption(CF_NICK, nick); | 74 | setstroption(CF_NICK, nick); |
| 69 | } | 75 | } |
| 70 | 76 | ||
| 71 | void own_channel_set( int channel ) { | 77 | void own_channel_set(int channel) { |
| 72 | if( channel != g_channel ) { | 78 | if (channel != g_channel) { |
| 73 | /* Remove all users from my chan, will be re-set on join message */ | 79 | /* Remove all users from my chan, will be re-set on join message */ |
| 74 | int i; | 80 | int i; |
| 75 | for( i=0; i<g_users_count; ++i ) | 81 | for (i = 0; i < g_users_count; ++i) |
| 76 | g_users[i].flags &= ~UL_IN_MY_CHAN; | 82 | g_users[i].flags &= ~UL_IN_MY_CHAN; |
| 77 | } | 83 | } |
| 78 | 84 | ||
| 79 | g_channel = channel; | 85 | g_channel = channel; |
| 80 | } | 86 | } |
| 81 | 87 | ||
| 82 | char const *own_nick_get( ) { | 88 | char const *own_nick_get() { return g_nick; } |
| 83 | return g_nick; | ||
| 84 | } | ||
| 85 | 89 | ||
| 86 | int own_nick_check( char *nick ) { | 90 | int own_nick_check(char *nick) { |
| 87 | if( !g_nick ) return -1; | 91 | if (!g_nick) |
| 88 | return !strcasecmp(g_nick,nick); | 92 | return -1; |
| 93 | return !strcasecmp(g_nick, nick); | ||
| 89 | } | 94 | } |
| 90 | 95 | ||
| 91 | int own_channel_get( ) { | 96 | int own_channel_get() { return g_channel; } |
| 92 | return g_channel; | ||
| 93 | } | ||
| 94 | 97 | ||
| 95 | /* Add/remove/rename */ | 98 | /* Add/remove/rename */ |
| 96 | int ul_add(char *name, int in_my_chan_flag ) { | 99 | int ul_add(char *name, int in_my_chan_flag) { |
| 97 | 100 | ||
| 98 | /* Test if user is already known */ | 101 | /* Test if user is already known */ |
| 99 | int exact_match, base = ul_nick_lookup( name, &exact_match ); | 102 | int exact_match, base = ul_nick_lookup(name, &exact_match); |
| 100 | if( !exact_match ) { | 103 | if (!exact_match) { |
| 101 | /* Make space for new user */ | 104 | /* Make space for new user */ |
| 102 | user * new_users = realloc( g_users, sizeof( user ) * ( 1 + g_users_count ) ); | 105 | user *new_users = realloc(g_users, sizeof(user) * (1 + g_users_count)); |
| 103 | if( !new_users ) return -1; | 106 | if (!new_users) |
| 107 | return -1; | ||
| 104 | 108 | ||
| 105 | /* Copy the tail */ | 109 | /* Copy the tail */ |
| 106 | g_users = new_users; | 110 | g_users = new_users; |
| 107 | memmove( g_users + base + 1, g_users + base, ( g_users_count - base ) * sizeof( user ) ); | 111 | memmove(g_users + base + 1, g_users + base, |
| 108 | g_users[base].nick = strdup( name ); | 112 | (g_users_count - base) * sizeof(user)); |
| 109 | g_users[base].flags = UL_NONE; | 113 | g_users[base].nick = strdup(name); |
| 110 | g_users[base].last_public = 0; | 114 | g_users[base].flags = UL_NONE; |
| 115 | g_users[base].last_public = 0; | ||
| 111 | g_users[base].last_private = 0; | 116 | g_users[base].last_private = 0; |
| 112 | 117 | ||
| 113 | g_users_count++; | 118 | g_users_count++; |
| 114 | } | 119 | } |
| 115 | 120 | ||
| 116 | g_users[base].flags &= ~UL_NOT_IN_LIST; | 121 | g_users[base].flags &= ~UL_NOT_IN_LIST; |
| 117 | switch( in_my_chan_flag ) { | 122 | switch (in_my_chan_flag) { |
| 118 | case 1: g_users[base].flags |= UL_IN_MY_CHAN; break; | 123 | case 1: |
| 119 | case 0: g_users[base].flags &= ~UL_IN_MY_CHAN; break; | 124 | g_users[base].flags |= UL_IN_MY_CHAN; |
| 120 | case -1: default: break; | 125 | break; |
| 126 | case 0: | ||
| 127 | g_users[base].flags &= ~UL_IN_MY_CHAN; | ||
| 128 | break; | ||
| 129 | case -1: | ||
| 130 | default: | ||
| 131 | break; | ||
| 121 | } | 132 | } |
| 122 | 133 | ||
| 123 | return base; | 134 | return base; |
| @@ -125,30 +136,33 @@ int ul_add(char *name, int in_my_chan_flag ) { | |||
| 125 | 136 | ||
| 126 | int ul_del(char *name) { | 137 | int ul_del(char *name) { |
| 127 | /* Test if user is already known */ | 138 | /* Test if user is already known */ |
| 128 | int exact_match, base = ul_nick_lookup( name, &exact_match ); | 139 | int exact_match, base = ul_nick_lookup(name, &exact_match); |
| 129 | if( !exact_match ) return -1; | 140 | if (!exact_match) |
| 141 | return -1; | ||
| 130 | 142 | ||
| 131 | /* Release the name buffer */ | 143 | /* Release the name buffer */ |
| 132 | free( g_users[base].nick ); | 144 | free(g_users[base].nick); |
| 133 | if( g_users[base].flags & UL_ME ) g_nick = 0; | 145 | if (g_users[base].flags & UL_ME) |
| 146 | g_nick = 0; | ||
| 134 | 147 | ||
| 135 | /* Copy the tail */ | 148 | /* Copy the tail */ |
| 136 | memmove( g_users + base, g_users + base + 1, ( g_users_count - base - 1 ) * sizeof( user ) ); | 149 | memmove(g_users + base, g_users + base + 1, |
| 150 | (g_users_count - base - 1) * sizeof(user)); | ||
| 137 | 151 | ||
| 138 | /* Shrink user list, realloc to a smaller size never fails */ | 152 | /* Shrink user list, realloc to a smaller size never fails */ |
| 139 | g_users = realloc( g_users, sizeof( user ) * --g_users_count ); | 153 | g_users = realloc(g_users, sizeof(user) * --g_users_count); |
| 140 | return 0; | 154 | return 0; |
| 141 | } | 155 | } |
| 142 | 156 | ||
| 143 | int ul_rename(char *oldname, char *newname) { | 157 | int ul_rename(char *oldname, char *newname) { |
| 144 | /* Ensure user */ | 158 | /* Ensure user */ |
| 145 | int base = ul_add( oldname, -1 ); | 159 | int base = ul_add(oldname, -1); |
| 146 | if( base >= 0 ) { | 160 | if (base >= 0) { |
| 147 | free( g_users[base].nick ); | 161 | free(g_users[base].nick); |
| 148 | g_users[base].nick = strdup( newname ); | 162 | g_users[base].nick = strdup(newname); |
| 149 | if( g_users[base].flags & UL_ME ) | 163 | if (g_users[base].flags & UL_ME) |
| 150 | g_nick = g_users[base].nick; | 164 | g_nick = g_users[base].nick; |
| 151 | if( g_users[base].flags & UL_IN_MY_CHAN ) | 165 | if (g_users[base].flags & UL_IN_MY_CHAN) |
| 152 | ul_public_action(newname); | 166 | ul_public_action(newname); |
| 153 | } | 167 | } |
| 154 | return base; | 168 | return base; |
| @@ -156,23 +170,23 @@ int ul_rename(char *oldname, char *newname) { | |||
| 156 | 170 | ||
| 157 | void ul_clear() { | 171 | void ul_clear() { |
| 158 | int i; | 172 | int i; |
| 159 | for( i=0; i<g_users_count; ++i ) | 173 | for (i = 0; i < g_users_count; ++i) |
| 160 | free( g_users[i].nick ); | 174 | free(g_users[i].nick); |
| 161 | free( g_users ); | 175 | free(g_users); |
| 162 | g_nick = 0; | 176 | g_nick = 0; |
| 163 | } | 177 | } |
| 164 | 178 | ||
| 165 | void ul_rebuild_list( ) { | 179 | void ul_rebuild_list() { |
| 166 | int i; | 180 | int i; |
| 167 | for( i=0; i<g_users_count; ++i ) | 181 | for (i = 0; i < g_users_count; ++i) |
| 168 | g_users[i].flags |= UL_NOT_IN_LIST; | 182 | g_users[i].flags |= UL_NOT_IN_LIST; |
| 169 | } | 183 | } |
| 170 | 184 | ||
| 171 | void ul_clean() { | 185 | void ul_clean() { |
| 172 | int i; | 186 | int i; |
| 173 | for( i=0; i<g_users_count; ++i ) { | 187 | for (i = 0; i < g_users_count; ++i) { |
| 174 | if( g_users[i].flags & UL_NOT_IN_LIST ) { | 188 | if (g_users[i].flags & UL_NOT_IN_LIST) { |
| 175 | ul_del( g_users[i].nick ); | 189 | ul_del(g_users[i].nick); |
| 176 | --i; | 190 | --i; |
| 177 | } | 191 | } |
| 178 | } | 192 | } |
| @@ -187,237 +201,270 @@ void ul_leave_chan(char *name) { | |||
| 187 | void ul_enter_chan(char *name) { | 201 | void ul_enter_chan(char *name) { |
| 188 | /* Ensure user and put him on the channel */ | 202 | /* Ensure user and put him on the channel */ |
| 189 | int base = ul_add(name, 1); | 203 | int base = ul_add(name, 1); |
| 190 | if( base >= 0 ) | 204 | if (base >= 0) |
| 191 | ul_public_action(name); | 205 | ul_public_action(name); |
| 192 | 206 | ||
| 193 | /* Reflect in UI */ | 207 | /* Reflect in UI */ |
| 194 | if( own_nick_check( name ) ) | 208 | if (own_nick_check(name)) |
| 195 | ownjoin( g_channel ); | 209 | ownjoin(g_channel); |
| 196 | } | 210 | } |
| 197 | 211 | ||
| 198 | void ul_private_action(char *name) { | 212 | void ul_private_action(char *name) { |
| 199 | /* Ensure user and keep channel state */ | 213 | /* Ensure user and keep channel state */ |
| 200 | int base = ul_add(name, -1); | 214 | int base = ul_add(name, -1); |
| 201 | if( base >= 0 ) | 215 | if (base >= 0) |
| 202 | g_users[base].last_private = ul_now(); | 216 | g_users[base].last_private = ul_now(); |
| 203 | } | 217 | } |
| 204 | 218 | ||
| 205 | void ul_public_action(char *name) { | 219 | void ul_public_action(char *name) { |
| 206 | /* Ensure user and put him on the channel */ | 220 | /* Ensure user and put him on the channel */ |
| 207 | int base = ul_add(name, 1); | 221 | int base = ul_add(name, 1); |
| 208 | if( base >= 0 ) | 222 | if (base >= 0) |
| 209 | g_users[base].last_public = ul_now(); | 223 | g_users[base].last_public = ul_now(); |
| 210 | } | 224 | } |
| 211 | 225 | ||
| 212 | void ul_add_to_dict(char *dict_items) { | 226 | void ul_add_to_dict(char *dict_items) { |
| 213 | char *i; | 227 | char *i; |
| 214 | for(i=strtok(dict_items," ");i;i=strtok(0," ")) { | 228 | for (i = strtok(dict_items, " "); i; i = strtok(0, " ")) { |
| 215 | g_dict = realloc( g_dict, sizeof(char*) * ( 1 + g_dict_len ) ); | 229 | g_dict = realloc(g_dict, sizeof(char *) * (1 + g_dict_len)); |
| 216 | if( !g_dict ) exit(1); | 230 | if (!g_dict) |
| 231 | exit(1); | ||
| 217 | g_dict[g_dict_len++] = strdup(i); | 232 | g_dict[g_dict_len++] = strdup(i); |
| 218 | } | 233 | } |
| 219 | } | 234 | } |
| 220 | 235 | ||
| 221 | /* Finding users ul_finduser? */ | 236 | /* Finding users ul_finduser? */ |
| 222 | char * ul_match_user(char *regex) { | 237 | char *ul_match_user(char *regex) { |
| 223 | char *dest = tmpstr; | 238 | char *dest = tmpstr; |
| 224 | int i; | 239 | int i; |
| 225 | regex_t preg; | 240 | regex_t preg; |
| 226 | 241 | ||
| 227 | *dest = 0; | 242 | *dest = 0; |
| 228 | if( !regcomp( &preg, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { | 243 | if (!regcomp(&preg, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) { |
| 229 | 244 | ||
| 230 | /* does the username match? */ | 245 | /* does the username match? */ |
| 231 | /* XXX overflow for too many matches */ | 246 | /* XXX overflow for too many matches */ |
| 232 | for( i=0; i<g_users_count; ++i ) | 247 | for (i = 0; i < g_users_count; ++i) |
| 233 | if( !regexec( &preg, g_users[i].nick, 0, NULL, 0)) /* append username to list */ | 248 | if (!regexec(&preg, g_users[i].nick, 0, NULL, |
| 234 | dest += snprintf ( dest, 256, " %s", g_users[i].nick); | 249 | 0)) /* append username to list */ |
| 235 | 250 | dest += snprintf(dest, 256, " %s", g_users[i].nick); | |
| 236 | } | 251 | } |
| 237 | regfree( &preg ); | 252 | regfree(&preg); |
| 238 | return tmpstr; | 253 | return tmpstr; |
| 239 | } | 254 | } |
| 240 | 255 | ||
| 241 | static int ul_compare_private( const void *a, const void *b ) { | 256 | static int ul_compare_private(const void *a, const void *b) { |
| 242 | const user *_a = (const user *)a, *_b = (const user *)b; | 257 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 243 | if( _a->last_private > _b->last_private ) return -1; | 258 | if (_a->last_private > _b->last_private) |
| 259 | return -1; | ||
| 244 | return 1; | 260 | return 1; |
| 245 | } | 261 | } |
| 246 | 262 | ||
| 247 | static int ul_compare_begin_of_line_ncase( const void *a, const void *b ) { | 263 | static int ul_compare_begin_of_line_ncase(const void *a, const void *b) { |
| 248 | const user *_a = (const user *)a, *_b = (const user *)b; | 264 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 249 | size_t tmpstr_len; | 265 | size_t tmpstr_len; |
| 250 | int a_i, b_i; | 266 | int a_i, b_i; |
| 251 | 267 | ||
| 252 | /* First ensure that users in current channel win */ | 268 | /* First ensure that users in current channel win */ |
| 253 | if( !(_a->flags & UL_IN_MY_CHAN ) ) return 1; | 269 | if (!(_a->flags & UL_IN_MY_CHAN)) |
| 254 | if( !(_b->flags & UL_IN_MY_CHAN ) ) return -1; | 270 | return 1; |
| 255 | 271 | if (!(_b->flags & UL_IN_MY_CHAN)) | |
| 256 | tmpstr_len = strlen( tmpstr ); | 272 | return -1; |
| 257 | a_i = strncasecmp( _a->nick, tmpstr, tmpstr_len ); | 273 | |
| 258 | b_i = strncasecmp( _b->nick, tmpstr, tmpstr_len ); | 274 | tmpstr_len = strlen(tmpstr); |
| 259 | 275 | a_i = strncasecmp(_a->nick, tmpstr, tmpstr_len); | |
| 260 | if( a_i && b_i ) return 0; // Both nicks dont match | 276 | b_i = strncasecmp(_b->nick, tmpstr, tmpstr_len); |
| 261 | if( !a_i && b_i ) return -1; // a matches insensitive, b doesnt | 277 | |
| 262 | if( a_i && !b_i ) return 1; // b matches insensitive, a doesnt | 278 | if (a_i && b_i) |
| 279 | return 0; // Both nicks dont match | ||
| 280 | if (!a_i && b_i) | ||
| 281 | return -1; // a matches insensitive, b doesnt | ||
| 282 | if (a_i && !b_i) | ||
| 283 | return 1; // b matches insensitive, a doesnt | ||
| 263 | 284 | ||
| 264 | /* From here both nicks match the prefix, ensure that own_nick | 285 | /* From here both nicks match the prefix, ensure that own_nick |
| 265 | always appears last */ | 286 | always appears last */ |
| 266 | if( _a->flags & UL_ME ) return 1; | 287 | if (_a->flags & UL_ME) |
| 267 | if( _b->flags & UL_ME ) return -1; | 288 | return 1; |
| 289 | if (_b->flags & UL_ME) | ||
| 290 | return -1; | ||
| 268 | 291 | ||
| 269 | /* Now the user with the most recent public activity wins */ | 292 | /* Now the user with the most recent public activity wins */ |
| 270 | if( _a->last_public > _b->last_public ) return -1; | 293 | if (_a->last_public > _b->last_public) |
| 294 | return -1; | ||
| 271 | 295 | ||
| 272 | return 1; | 296 | return 1; |
| 273 | } | 297 | } |
| 274 | 298 | ||
| 275 | static int ul_compare_begin_of_line_case( const void *a, const void *b ) { | 299 | static int ul_compare_begin_of_line_case(const void *a, const void *b) { |
| 276 | const user *_a = (const user *)a, *_b = (const user *)b; | 300 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 277 | size_t tmpstr_len; | 301 | size_t tmpstr_len; |
| 278 | int a_i, b_i, a_s, b_s; | 302 | int a_i, b_i, a_s, b_s; |
| 279 | 303 | ||
| 280 | /* First ensure that users in current channel win */ | 304 | /* First ensure that users in current channel win */ |
| 281 | if( !(_a->flags & UL_IN_MY_CHAN ) ) return 1; | 305 | if (!(_a->flags & UL_IN_MY_CHAN)) |
| 282 | if( !(_b->flags & UL_IN_MY_CHAN ) ) return -1; | 306 | return 1; |
| 283 | 307 | if (!(_b->flags & UL_IN_MY_CHAN)) | |
| 284 | tmpstr_len = strlen( tmpstr ); | 308 | return -1; |
| 285 | a_i = strncasecmp( _a->nick, tmpstr, tmpstr_len ); | 309 | |
| 286 | a_s = strncmp ( _a->nick, tmpstr, tmpstr_len ); | 310 | tmpstr_len = strlen(tmpstr); |
| 287 | b_i = strncasecmp( _b->nick, tmpstr, tmpstr_len ); | 311 | a_i = strncasecmp(_a->nick, tmpstr, tmpstr_len); |
| 288 | b_s = strncmp ( _b->nick, tmpstr, tmpstr_len ); | 312 | a_s = strncmp(_a->nick, tmpstr, tmpstr_len); |
| 289 | 313 | b_i = strncasecmp(_b->nick, tmpstr, tmpstr_len); | |
| 290 | if( a_i && b_i ) return 0; // Both nicks dont match at all | 314 | b_s = strncmp(_b->nick, tmpstr, tmpstr_len); |
| 291 | if( !a_i && b_i ) return -1; // a matches insensitive, b doesnt | 315 | |
| 292 | if( a_i && !b_i ) return 1; // b matches insensitive, a doesnt | 316 | if (a_i && b_i) |
| 293 | 317 | return 0; // Both nicks dont match at all | |
| 294 | if( !a_s && b_s ) return -1; // a matches sensitive, b doesnt | 318 | if (!a_i && b_i) |
| 295 | if( a_s && !b_s ) return 1; // b matches sensitive, a doesnt | 319 | return -1; // a matches insensitive, b doesnt |
| 320 | if (a_i && !b_i) | ||
| 321 | return 1; // b matches insensitive, a doesnt | ||
| 322 | |||
| 323 | if (!a_s && b_s) | ||
| 324 | return -1; // a matches sensitive, b doesnt | ||
| 325 | if (a_s && !b_s) | ||
| 326 | return 1; // b matches sensitive, a doesnt | ||
| 296 | 327 | ||
| 297 | /* From now we know that both match with same quality, ensure | 328 | /* From now we know that both match with same quality, ensure |
| 298 | that own nick always appears last */ | 329 | that own nick always appears last */ |
| 299 | if( _a->flags & UL_ME ) return 1; | 330 | if (_a->flags & UL_ME) |
| 300 | if( _b->flags & UL_ME ) return -1; | 331 | return 1; |
| 332 | if (_b->flags & UL_ME) | ||
| 333 | return -1; | ||
| 301 | 334 | ||
| 302 | /* Now the user with the most recent public activity wins */ | 335 | /* Now the user with the most recent public activity wins */ |
| 303 | if( _a->last_public > _b->last_public ) return -1; | 336 | if (_a->last_public > _b->last_public) |
| 337 | return -1; | ||
| 304 | 338 | ||
| 305 | return 1; | 339 | return 1; |
| 306 | } | 340 | } |
| 307 | 341 | ||
| 308 | static int ul_compare_middle_ncase( const void *a, const void *b ) { | 342 | static int ul_compare_middle_ncase(const void *a, const void *b) { |
| 309 | const user *_a = (const user *)a, *_b = (const user *)b; | 343 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 310 | 344 | ||
| 311 | /* Ensure that own nick appears last in list */ | 345 | /* Ensure that own nick appears last in list */ |
| 312 | if( _a->flags & UL_ME ) return 1; | 346 | if (_a->flags & UL_ME) |
| 313 | if( _b->flags & UL_ME ) return -1; | 347 | return 1; |
| 348 | if (_b->flags & UL_ME) | ||
| 349 | return -1; | ||
| 314 | 350 | ||
| 315 | return strcasecmp( _a->nick, _b->nick ); | 351 | return strcasecmp(_a->nick, _b->nick); |
| 316 | } | 352 | } |
| 317 | 353 | ||
| 318 | static int ul_compare_middle_case( const void *a, const void *b ) { | 354 | static int ul_compare_middle_case(const void *a, const void *b) { |
| 319 | const user *_a = (const user *)a, *_b = (const user *)b; | 355 | const user *_a = (const user *)a, *_b = (const user *)b; |
| 320 | size_t tmpstr_len; | 356 | size_t tmpstr_len; |
| 321 | int a_s, b_s; | 357 | int a_s, b_s; |
| 322 | 358 | ||
| 323 | /* Ensure that own nick appears last in list */ | 359 | /* Ensure that own nick appears last in list */ |
| 324 | if( _a->flags & UL_ME ) return 1; | 360 | if (_a->flags & UL_ME) |
| 325 | if( _b->flags & UL_ME ) return -1; | 361 | return 1; |
| 362 | if (_b->flags & UL_ME) | ||
| 363 | return -1; | ||
| 326 | 364 | ||
| 327 | tmpstr_len = strlen( tmpstr ); | 365 | tmpstr_len = strlen(tmpstr); |
| 328 | a_s = strncmp( _a->nick, tmpstr, tmpstr_len ); | 366 | a_s = strncmp(_a->nick, tmpstr, tmpstr_len); |
| 329 | b_s = strncmp( _b->nick, tmpstr, tmpstr_len ); | 367 | b_s = strncmp(_b->nick, tmpstr, tmpstr_len); |
| 330 | 368 | ||
| 331 | if( !a_s && b_s ) return -1; // a matches sensitive, b doesnt | 369 | if (!a_s && b_s) |
| 332 | if( a_s && !b_s ) return 1; // b matches sensitive, a doesnt | 370 | return -1; // a matches sensitive, b doesnt |
| 371 | if (a_s && !b_s) | ||
| 372 | return 1; // b matches sensitive, a doesnt | ||
| 333 | 373 | ||
| 334 | /* From now both strings either both or both dont match | 374 | /* From now both strings either both or both dont match |
| 335 | decide their position by case insensitive match */ | 375 | decide their position by case insensitive match */ |
| 336 | return strcasecmp( _a->nick, _b->nick ); | 376 | return strcasecmp(_a->nick, _b->nick); |
| 337 | } | 377 | } |
| 338 | 378 | ||
| 339 | /* Nick completion function for readline */ | 379 | /* Nick completion function for readline */ |
| 340 | char **ul_complete_user(char *text, int start, int end ) { | 380 | char **ul_complete_user(char *text, int start, int end) { |
| 341 | char **result = 0; | 381 | char **result = 0; |
| 342 | int i, result_count = 0, dict_result_count = 0; | 382 | int i, result_count = 0, dict_result_count = 0; |
| 343 | 383 | ||
| 344 | /* Never want readline to complete filenames */ | 384 | /* Never want readline to complete filenames */ |
| 345 | rl_attempted_completion_over = 1; | 385 | rl_attempted_completion_over = 1; |
| 346 | 386 | ||
| 347 | /* Check for amount of custom dict matches */ | 387 | /* Check for amount of custom dict matches */ |
| 348 | if( end && ( start != end ) ) | 388 | if (end && (start != end)) |
| 349 | for( i=0; i<g_dict_len; ++i ) | 389 | for (i = 0; i < g_dict_len; ++i) |
| 350 | if( !strncasecmp( g_dict[i], text+start, end-start ) ) | 390 | if (!strncasecmp(g_dict[i], text + start, end - start)) |
| 351 | ++dict_result_count; | 391 | ++dict_result_count; |
| 352 | 392 | ||
| 353 | /* Prepare return array ... of max g_users_count (char*) | 393 | /* Prepare return array ... of max g_users_count (char*) |
| 354 | Plus least common prefix in [0] and null terminator | 394 | Plus least common prefix in [0] and null terminator |
| 355 | */ | 395 | */ |
| 356 | result = malloc( sizeof(char*) * ( 2 + g_users_count + dict_result_count ) ); | 396 | result = malloc(sizeof(char *) * (2 + g_users_count + dict_result_count)); |
| 357 | if( !result ) return 0; | 397 | if (!result) |
| 398 | return 0; | ||
| 358 | 399 | ||
| 359 | if( start == 0 && end == 0 ) { | 400 | if (start == 0 && end == 0) { |
| 360 | /* Completion on begin of line yields list of everyone we | 401 | /* Completion on begin of line yields list of everyone we |
| 361 | were in private conversation, sorted by time of last .m */ | 402 | were in private conversation, sorted by time of last .m */ |
| 362 | qsort( g_users, g_users_count, sizeof(user), ul_compare_private ); | 403 | qsort(g_users, g_users_count, sizeof(user), ul_compare_private); |
| 363 | for( i=0; i<g_users_count; ++i ) | 404 | for (i = 0; i < g_users_count; ++i) |
| 364 | if( g_users[i].last_private ) { | 405 | if (g_users[i].last_private) { |
| 365 | snprintf( tmpstr, TMPSTRSIZE, ".m %s", g_users[i].nick ); | 406 | snprintf(tmpstr, TMPSTRSIZE, ".m %s", g_users[i].nick); |
| 366 | result[++result_count] = strdup(tmpstr); | 407 | result[++result_count] = strdup(tmpstr); |
| 367 | } | 408 | } |
| 368 | /* No common prefix */ | 409 | /* No common prefix */ |
| 369 | if( result_count ) result[0] = strdup(""); | 410 | if (result_count) |
| 411 | result[0] = strdup(""); | ||
| 370 | 412 | ||
| 371 | } else if( start == 0 && end > 0 ) { | 413 | } else if (start == 0 && end > 0) { |
| 372 | /* Completion on begin of line with some chars already typed yields | 414 | /* Completion on begin of line with some chars already typed yields |
| 373 | a list of everyone in channel, matching prefix, sorted by last | 415 | a list of everyone in channel, matching prefix, sorted by last |
| 374 | public activity */ | 416 | public activity */ |
| 375 | snprintf( tmpstr, end + 1, "%s", text ); | 417 | snprintf(tmpstr, end + 1, "%s", text); |
| 376 | if( ul_case_first ) | 418 | if (ul_case_first) |
| 377 | qsort( g_users, g_users_count, sizeof(user), ul_compare_begin_of_line_case ); | 419 | qsort(g_users, g_users_count, sizeof(user), |
| 420 | ul_compare_begin_of_line_case); | ||
| 378 | else | 421 | else |
| 379 | qsort( g_users, g_users_count, sizeof(user), ul_compare_begin_of_line_ncase ); | 422 | qsort(g_users, g_users_count, sizeof(user), |
| 423 | ul_compare_begin_of_line_ncase); | ||
| 380 | 424 | ||
| 381 | for( i=0; i<g_users_count; ++i ) | 425 | for (i = 0; i < g_users_count; ++i) |
| 382 | if( ( g_users[i].flags & UL_IN_MY_CHAN ) && !strncasecmp( g_users[i].nick, tmpstr, end ) ) { | 426 | if ((g_users[i].flags & UL_IN_MY_CHAN) && |
| 383 | snprintf( tmpstr, TMPSTRSIZE, "%s:", g_users[i].nick ); | 427 | !strncasecmp(g_users[i].nick, tmpstr, end)) { |
| 428 | snprintf(tmpstr, TMPSTRSIZE, "%s:", g_users[i].nick); | ||
| 384 | result[++result_count] = strdup(tmpstr); | 429 | result[++result_count] = strdup(tmpstr); |
| 385 | } | 430 | } |
| 386 | 431 | ||
| 387 | /* Copy matches from personal dict to the end */ | 432 | /* Copy matches from personal dict to the end */ |
| 388 | for( i=0; i<g_dict_len; ++i ) | 433 | for (i = 0; i < g_dict_len; ++i) |
| 389 | if( !strncasecmp( g_dict[i], tmpstr, end-start ) ) { | 434 | if (!strncasecmp(g_dict[i], tmpstr, end - start)) { |
| 390 | snprintf( tmpstr, TMPSTRSIZE, "%s:", g_dict[i] ); | 435 | snprintf(tmpstr, TMPSTRSIZE, "%s:", g_dict[i]); |
| 391 | result[++result_count] = strdup(tmpstr); | 436 | result[++result_count] = strdup(tmpstr); |
| 392 | } | 437 | } |
| 393 | 438 | ||
| 394 | /* Copy common prefix */ | 439 | /* Copy common prefix */ |
| 395 | if( result_count ) result[0] = strndup(text, end); | 440 | if (result_count) |
| 396 | } else if( start != end ) { | 441 | result[0] = strndup(text, end); |
| 442 | } else if (start != end) { | ||
| 397 | /* Completion in the middle of the line most likely is a .m XY<TAB> | 443 | /* Completion in the middle of the line most likely is a .m XY<TAB> |
| 398 | and thus should complete all users, sorted alphabetically without | 444 | and thus should complete all users, sorted alphabetically without |
| 399 | preferences. */ | 445 | preferences. */ |
| 400 | snprintf( tmpstr, end - start + 1, "%s", text ); | 446 | snprintf(tmpstr, end - start + 1, "%s", text); |
| 401 | if( ul_case_first ) | 447 | if (ul_case_first) |
| 402 | qsort( g_users, g_users_count, sizeof(user), ul_compare_middle_case ); | 448 | qsort(g_users, g_users_count, sizeof(user), ul_compare_middle_case); |
| 403 | else | 449 | else |
| 404 | qsort( g_users, g_users_count, sizeof(user), ul_compare_middle_ncase ); | 450 | qsort(g_users, g_users_count, sizeof(user), ul_compare_middle_ncase); |
| 405 | 451 | ||
| 406 | for( i=0; i<g_users_count; ++i ) | 452 | for (i = 0; i < g_users_count; ++i) |
| 407 | if( !strncasecmp( g_users[i].nick, tmpstr, end - start ) ) | 453 | if (!strncasecmp(g_users[i].nick, tmpstr, end - start)) |
| 408 | result[++result_count] = strdup(g_users[i].nick); | 454 | result[++result_count] = strdup(g_users[i].nick); |
| 409 | 455 | ||
| 410 | /* Copy matches from personal dict to the end */ | 456 | /* Copy matches from personal dict to the end */ |
| 411 | for( i=0; i<g_dict_len; ++i ) | 457 | for (i = 0; i < g_dict_len; ++i) |
| 412 | if( !strncasecmp( g_dict[i], tmpstr, end-start ) ) | 458 | if (!strncasecmp(g_dict[i], tmpstr, end - start)) |
| 413 | result[++result_count] = strdup(g_dict[i]); | 459 | result[++result_count] = strdup(g_dict[i]); |
| 414 | 460 | ||
| 415 | /* Copy common prefix */ | 461 | /* Copy common prefix */ |
| 416 | if( result_count ) result[0] = strndup(text, end - start); | 462 | if (result_count) |
| 463 | result[0] = strndup(text, end - start); | ||
| 417 | } /* else: completion of an empty word in the middle yields nothing */ | 464 | } /* else: completion of an empty word in the middle yields nothing */ |
| 418 | 465 | ||
| 419 | if( !result_count ) { | 466 | if (!result_count) { |
| 420 | free( result ); | 467 | free(result); |
| 421 | result = 0; | 468 | result = 0; |
| 422 | } else | 469 | } else |
| 423 | result[++result_count] = 0; | 470 | result[++result_count] = 0; |
