diff options
| author | erdgeist <> | 2010-10-09 03:58:43 +0000 |
|---|---|---|
| committer | erdgeist <> | 2010-10-09 03:58:43 +0000 |
| commit | db52d59bd2769cc4cc81a20bfd58c07ed34d619c (patch) | |
| tree | c4ec38a65bb95d112879e0bae37d675d608f47a1 | |
| parent | a468c875f7a35c595b2eb575ad99354e3e9c3662 (diff) | |
Handle utf8 in readline correctly
| -rwxr-xr-x | vchat-ui.c | 142 |
1 files changed, 87 insertions, 55 deletions
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <openssl/pem.h> | 31 | #include <openssl/pem.h> |
| 32 | #include <regex.h> | 32 | #include <regex.h> |
| 33 | #include "vchat.h" | 33 | #include "vchat.h" |
| 34 | #include <wchar.h> | ||
| 34 | 35 | ||
| 35 | /* version of this module */ | 36 | /* version of this module */ |
| 36 | char *vchat_ui_version = "$Id$"; | 37 | char *vchat_ui_version = "$Id$"; |
| @@ -54,8 +55,6 @@ static WINDOW *output = NULL; | |||
| 54 | /* our screen dimensions */ | 55 | /* our screen dimensions */ |
| 55 | static int screensx = 0; | 56 | static int screensx = 0; |
| 56 | static int screensy = 0; | 57 | static int screensy = 0; |
| 57 | /* length of last input on line (for clearing) */ | ||
| 58 | static int lastlen = 0; | ||
| 59 | /* current horizontal scrolling offset for input line */ | 58 | /* current horizontal scrolling offset for input line */ |
| 60 | static int scroff = 0; | 59 | static int scroff = 0; |
| 61 | /* cache for stepping value of horizontal scrolling */ | 60 | /* cache for stepping value of horizontal scrolling */ |
| @@ -140,6 +139,35 @@ togglequery() { | |||
| 140 | } | 139 | } |
| 141 | } | 140 | } |
| 142 | 141 | ||
| 142 | const char * skip_to_character( const char * string, size_t offset ) { | ||
| 143 | mbstate_t mbs; | ||
| 144 | memset(&mbs, 0, sizeof(mbs)); | ||
| 145 | |||
| 146 | while( offset-- > 0 ) { | ||
| 147 | size_t ch_size = mbrlen( string, MB_CUR_MAX, &mbs ); | ||
| 148 | if( ch_size < 0 ) return NULL; | ||
| 149 | if( !ch_size ) break; | ||
| 150 | string += ch_size; | ||
| 151 | } | ||
| 152 | return string; | ||
| 153 | } | ||
| 154 | |||
| 155 | size_t offset_to_character( const char * string, size_t offset ) { | ||
| 156 | mbstate_t mbs; | ||
| 157 | memset(&mbs, 0, sizeof(mbs)); | ||
| 158 | const char * string_offset = string + offset; | ||
| 159 | size_t nchars = 0; | ||
| 160 | |||
| 161 | while( string < string_offset ) { | ||
| 162 | size_t ch_size = mbrlen( string, MB_CUR_MAX, &mbs ); | ||
| 163 | if( ch_size < 0 ) return -1; | ||
| 164 | if( !ch_size ) break; | ||
| 165 | string += ch_size; | ||
| 166 | nchars++; | ||
| 167 | } | ||
| 168 | return nchars; | ||
| 169 | } | ||
| 170 | |||
| 143 | /* readlines callback when a line is completed */ | 171 | /* readlines callback when a line is completed */ |
| 144 | static void | 172 | static void |
| 145 | linecomplete (char *line) | 173 | linecomplete (char *line) |
| @@ -150,35 +178,31 @@ linecomplete (char *line) | |||
| 150 | /* send linefeed, return pointer, reset cursors */ | 178 | /* send linefeed, return pointer, reset cursors */ |
| 151 | waddch (input, '\n'); | 179 | waddch (input, '\n'); |
| 152 | wmove (input, 0, 0); | 180 | wmove (input, 0, 0); |
| 153 | rl_point = 0; | ||
| 154 | scroff = 0; | 181 | scroff = 0; |
| 155 | 182 | ||
| 156 | if (line) { | 183 | if (line) { |
| 157 | i = strlen(line)-1; | 184 | i = strlen(line) - 1; |
| 158 | while (line[i] == ' ') line[i--]='\0'; | 185 | while (line[i] == ' ') line[i--]='\0'; |
| 159 | 186 | ||
| 160 | if (line[0] && strchr(line,' ') == NULL && line[i] == ':') | 187 | if (line[0] && strchr(line,' ') == NULL && line[i] == ':') |
| 161 | line[i--] = '\0'; | 188 | line[i--] = '\0'; |
| 162 | |||
| 163 | /* empty line? nada. */ | ||
| 164 | if (!line[0]) | ||
| 165 | return; | ||
| 166 | 189 | ||
| 190 | /* empty line? nada. */ | ||
| 191 | if (line[0]) { | ||
| 167 | /* add line to history and have it handled in vchat-protocol.c */ | 192 | /* add line to history and have it handled in vchat-protocol.c */ |
| 168 | add_history (line); | 193 | add_history (line); |
| 169 | handleline (line); | 194 | handleline (line); |
| 170 | free (line); | 195 | } |
| 171 | 196 | free (line); | |
| 172 | /* If in query mode, feed query prefix */ | 197 | rl_reset_line_state(); |
| 173 | if (( c = querypartner )) | 198 | rl_point = rl_end = rl_done = 0; |
| 174 | while( *c ) rl_stuff_char( *c++ ); | 199 | |
| 175 | 200 | /* If in query mode, feed query prefix */ | |
| 176 | /* wipe input line and reset cursor */ | 201 | if (( c = querypartner )) |
| 177 | wmove (input, 0, 0); | 202 | while( *c ) rl_stuff_char( *c++ ); |
| 178 | for (i = 0; i < getmaxx(input) - 1; i++) | 203 | |
| 179 | waddch (input, ' '); | 204 | /* wipe input line and reset cursor */ |
| 180 | wmove (input, 0, 0); | 205 | wrefresh (input); |
| 181 | wrefresh (input); | ||
| 182 | } | 206 | } |
| 183 | } | 207 | } |
| 184 | 208 | ||
| @@ -187,36 +211,48 @@ static void | |||
| 187 | vciredraw (void) | 211 | vciredraw (void) |
| 188 | { | 212 | { |
| 189 | int i; | 213 | int i; |
| 214 | size_t readline_point; | ||
| 215 | |||
| 216 | /* readline offers us information we don't need | ||
| 217 | so ignore outabound cursor positions */ | ||
| 218 | if( rl_point < 0 ) rl_point = 0; | ||
| 219 | //if( rl_point > rl_end ) rl_point = rl_end; | ||
| 220 | |||
| 221 | readline_point = offset_to_character( rl_line_buffer, rl_point ); | ||
| 222 | |||
| 190 | /* hscroll value cache set up? */ | 223 | /* hscroll value cache set up? */ |
| 191 | if (!hscroll) | 224 | if (!hscroll) { |
| 192 | { | 225 | /* check config-option or set hardcoded default */ |
| 193 | /* check config-option or set hardcoded default */ | 226 | hscroll = getintoption (CF_HSCROLL); |
| 194 | hscroll = getintoption (CF_HSCROLL); | 227 | if (!hscroll) |
| 195 | if (!hscroll) | 228 | hscroll = 15; |
| 196 | hscroll = 5; | 229 | } |
| 197 | } | ||
| 198 | 230 | ||
| 199 | /* calculate horizontal scrolling offset */ | 231 | /* calculate horizontal scrolling offset */ |
| 200 | if (rl_point - scroff < 0) | 232 | |
| 201 | scroff = rl_point - 4; | 233 | /* Case 1: readline is left of current scroll offset: Adjust to left to reveal more text */ |
| 202 | if (rl_point - scroff > getmaxx(input) - 1 ) | 234 | if( readline_point < scroff ) |
| 203 | scroff = rl_point - getmaxx(input) + 1; | 235 | scroff = readline_point - hscroll; |
| 204 | if (rl_point - scroff > getmaxx(input) - 1 - (hscroll - 2)) | 236 | if( scroff < 1 ) |
| 205 | scroff += hscroll; | ||
| 206 | else if (rl_point - scroff < getmaxx(input) - 1 - (hscroll + 2)) | ||
| 207 | scroff -= hscroll; | ||
| 208 | if (scroff < 0) | ||
| 209 | scroff = 0; | 237 | scroff = 0; |
| 210 | 238 | ||
| 239 | /* Case 2: readline just hit the last char on the line: Adjust to right to leave more space on screen */ | ||
| 240 | if( readline_point >= scroff + getmaxx(input) - 1 ) | ||
| 241 | scroff = readline_point - getmaxx(input) + hscroll; | ||
| 242 | |||
| 211 | /* wipe input line */ | 243 | /* wipe input line */ |
| 212 | wmove (input, 0, 0); | 244 | wmove (input, 0, 0); |
| 213 | for (i = 0; i < getmaxx(input) - 1; i++) | 245 | for (i = 0; i < getmaxx(input) - 1; i++) |
| 214 | waddch (input, ' '); | 246 | waddch (input, ' '); |
| 215 | 247 | ||
| 216 | /* show current line, move cursor, redraw! */ | 248 | /* show current line, move cursor, redraw! */ |
| 217 | mvwaddnstr (input, 0, 0, &rl_line_buffer[scroff], getmaxx(input) - 1 ); | 249 | const char *start_line = skip_to_character( rl_line_buffer, scroff ); |
| 218 | wmove (input, 0, rl_point - scroff); | 250 | const char *end_line = skip_to_character( start_line, getmaxx(input) - 1 ); |
| 251 | |||
| 252 | mvwaddnstr (input, 0, 0, start_line, end_line - start_line ); | ||
| 253 | wmove (input, 0, readline_point - scroff ); | ||
| 219 | wrefresh (input); | 254 | wrefresh (input); |
| 255 | |||
| 220 | } | 256 | } |
| 221 | 257 | ||
| 222 | /* called by the eventloop in vchat-client.c */ | 258 | /* called by the eventloop in vchat-client.c */ |
| @@ -1375,17 +1411,15 @@ nickprompt (void) | |||
| 1375 | while (!nick || !nick[0]) | 1411 | while (!nick || !nick[0]) |
| 1376 | { | 1412 | { |
| 1377 | if (nick) | 1413 | if (nick) |
| 1378 | free (nick); | 1414 | free (nick); |
| 1379 | nick = readline(""); | 1415 | nick = readline(""); |
| 1380 | } | 1416 | } |
| 1381 | setstroption(CF_NICK,nick); | 1417 | setstroption(CF_NICK,nick); |
| 1382 | 1418 | ||
| 1383 | /* try to get readlines stats clean again */ | 1419 | /* try to get readlines stats clean again */ |
| 1384 | //rl_free_line_state (); | 1420 | //rl_free_line_state (); |
| 1385 | rl_point = 0; | 1421 | memset( rl_line_buffer, 0, rl_end ); |
| 1386 | rl_done = 0; | 1422 | rl_point = rl_end = rl_done = 0; |
| 1387 | rl_line_buffer[0] = 0; | ||
| 1388 | lastlen = 23; | ||
| 1389 | 1423 | ||
| 1390 | /* wipe input line and reset cursor */ | 1424 | /* wipe input line and reset cursor */ |
| 1391 | rl_kill_full_line(0,0); | 1425 | rl_kill_full_line(0,0); |
| @@ -1422,7 +1456,7 @@ passprompt (char *buf, int size, int rwflag, void *userdata) | |||
| 1422 | { | 1456 | { |
| 1423 | int i; | 1457 | int i; |
| 1424 | char *passphrase = NULL; | 1458 | char *passphrase = NULL; |
| 1425 | 1459 | ||
| 1426 | /* use special non-revealing redraw function */ | 1460 | /* use special non-revealing redraw function */ |
| 1427 | /* FIXME: passphrase isn't protected against e.g. swapping */ | 1461 | /* FIXME: passphrase isn't protected against e.g. swapping */ |
| 1428 | rl_redisplay_function = vcnredraw; | 1462 | rl_redisplay_function = vcnredraw; |
| @@ -1432,7 +1466,7 @@ passprompt (char *buf, int size, int rwflag, void *userdata) | |||
| 1432 | while (!passphrase || !passphrase[0]) | 1466 | while (!passphrase || !passphrase[0]) |
| 1433 | { | 1467 | { |
| 1434 | if (passphrase) | 1468 | if (passphrase) |
| 1435 | free (passphrase); | 1469 | free (passphrase); |
| 1436 | passphrase = readline (""); | 1470 | passphrase = readline (""); |
| 1437 | } | 1471 | } |
| 1438 | 1472 | ||
| @@ -1445,11 +1479,9 @@ passprompt (char *buf, int size, int rwflag, void *userdata) | |||
| 1445 | 1479 | ||
| 1446 | /* try to get readlines stats clean again */ | 1480 | /* try to get readlines stats clean again */ |
| 1447 | //rl_free_line_state (); | 1481 | //rl_free_line_state (); |
| 1448 | rl_point = 0; | 1482 | memset( rl_line_buffer, 0, rl_end ); |
| 1449 | rl_done = 0; | 1483 | rl_point = rl_end = rl_done = 0; |
| 1450 | rl_line_buffer[0] = 0; | 1484 | |
| 1451 | lastlen = 23; | ||
| 1452 | |||
| 1453 | /* wipe input line and reset cursor */ | 1485 | /* wipe input line and reset cursor */ |
| 1454 | wmove (input, 0, 0); | 1486 | wmove (input, 0, 0); |
| 1455 | for (i = 0; i < getmaxx(input) - 1; i++) | 1487 | for (i = 0; i < getmaxx(input) - 1; i++) |
| @@ -1482,7 +1514,7 @@ static int | |||
| 1482 | removefromfilterlist( int(*test)(filt *flt, void *data, char colour), void *data, char colour) { | 1514 | removefromfilterlist( int(*test)(filt *flt, void *data, char colour), void *data, char colour) { |
| 1483 | filt **flt = &filterlist, *tmp; | 1515 | filt **flt = &filterlist, *tmp; |
| 1484 | int removed = 0, stop = 0; | 1516 | int removed = 0, stop = 0; |
| 1485 | 1517 | ||
| 1486 | while( *flt && !stop ) { | 1518 | while( *flt && !stop ) { |
| 1487 | switch( test( *flt, data, colour ) ) { | 1519 | switch( test( *flt, data, colour ) ) { |
| 1488 | case RMFILTER_RMANDSTOP: /* remove */ | 1520 | case RMFILTER_RMANDSTOP: /* remove */ |
