Skip to content

Commit 47eec67

Browse files
zeertzjqbrammool
authored andcommitted
patch 9.0.1598: screenchar() and others are wrong with DBCS 'encoding'
Problem: screenchar(), screenchars() and screenstring() do not work properly when 'encoding' is set to a double-byte encoding. Solution: Fix the way the bytes of the characters are obtained. (issue #12469)
1 parent 8509014 commit 47eec67

5 files changed

Lines changed: 58 additions & 35 deletions

File tree

src/evalfunc.c

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8934,7 +8934,6 @@ f_screenchar(typval_T *argvars, typval_T *rettv)
89348934
{
89358935
int row;
89368936
int col;
8937-
int off;
89388937
int c;
89398938

89408939
if (in_vim9script()
@@ -8948,11 +8947,9 @@ f_screenchar(typval_T *argvars, typval_T *rettv)
89488947
c = -1;
89498948
else
89508949
{
8951-
off = LineOffset[row] + col;
8952-
if (enc_utf8 && ScreenLinesUC[off] != 0)
8953-
c = ScreenLinesUC[off];
8954-
else
8955-
c = ScreenLines[off];
8950+
char_u buf[MB_MAXBYTES + 1];
8951+
screen_getbytes(row, col, buf, NULL);
8952+
c = (*mb_ptr2char)(buf);
89568953
}
89578954
rettv->vval.v_number = c;
89588955
}
@@ -8965,7 +8962,6 @@ f_screenchars(typval_T *argvars, typval_T *rettv)
89658962
{
89668963
int row;
89678964
int col;
8968-
int off;
89698965
int c;
89708966
int i;
89718967

@@ -8982,18 +8978,18 @@ f_screenchars(typval_T *argvars, typval_T *rettv)
89828978
if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns)
89838979
return;
89848980

8985-
off = LineOffset[row] + col;
8986-
if (enc_utf8 && ScreenLinesUC[off] != 0)
8987-
c = ScreenLinesUC[off];
8981+
char_u buf[MB_MAXBYTES + 1];
8982+
screen_getbytes(row, col, buf, NULL);
8983+
int pcc[MAX_MCO];
8984+
if (enc_utf8)
8985+
c = utfc_ptr2char(buf, pcc);
89888986
else
8989-
c = ScreenLines[off];
8987+
c = (*mb_ptr2char)(buf);
89908988
list_append_number(rettv->vval.v_list, (varnumber_T)c);
89918989

89928990
if (enc_utf8)
8993-
8994-
for (i = 0; i < Screen_mco && ScreenLinesC[i][off] != 0; ++i)
8995-
list_append_number(rettv->vval.v_list,
8996-
(varnumber_T)ScreenLinesC[i][off]);
8991+
for (i = 0; i < Screen_mco && pcc[i] != 0; ++i)
8992+
list_append_number(rettv->vval.v_list, (varnumber_T)pcc[i]);
89978993
}
89988994

89998995
/*
@@ -9024,11 +9020,7 @@ f_screenstring(typval_T *argvars, typval_T *rettv)
90249020
{
90259021
int row;
90269022
int col;
9027-
int off;
9028-
int c;
9029-
int i;
90309023
char_u buf[MB_MAXBYTES + 1];
9031-
int buflen = 0;
90329024

90339025
rettv->vval.v_string = NULL;
90349026
rettv->v_type = VAR_STRING;
@@ -9043,18 +9035,7 @@ f_screenstring(typval_T *argvars, typval_T *rettv)
90439035
if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns)
90449036
return;
90459037

9046-
off = LineOffset[row] + col;
9047-
if (enc_utf8 && ScreenLinesUC[off] != 0)
9048-
c = ScreenLinesUC[off];
9049-
else
9050-
c = ScreenLines[off];
9051-
buflen += mb_char2bytes(c, buf);
9052-
9053-
if (enc_utf8 && ScreenLinesUC[off] != 0)
9054-
for (i = 0; i < Screen_mco && ScreenLinesC[i][off] != 0; ++i)
9055-
buflen += mb_char2bytes(ScreenLinesC[i][off], buf + buflen);
9056-
9057-
buf[buflen] = NUL;
9038+
screen_getbytes(row, col, buf, NULL);
90589039
rettv->vval.v_string = vim_strsave(buf);
90599040
}
90609041

@@ -9433,7 +9414,7 @@ f_searchpos(typval_T *argvars, typval_T *rettv)
94339414

94349415
/*
94359416
* Set the cursor or mark position.
9436-
* If 'charpos' is TRUE, then use the column number as a character offset.
9417+
* If "charpos" is TRUE, then use the column number as a character offset.
94379418
* Otherwise use the column number as a byte offset.
94389419
*/
94399420
static void

src/screen.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,8 +1199,9 @@ screen_putchar(int c, int row, int col, int attr)
11991199
}
12001200

12011201
/*
1202-
* Get a single character directly from ScreenLines into "bytes[]".
1203-
* Also return its attribute in *attrp;
1202+
* Get a single character directly from ScreenLines into "bytes", which must
1203+
* have a size of "MB_MAXBYTES + 1".
1204+
* If "attrp" is not NULL, return the character's attribute in "*attrp".
12041205
*/
12051206
void
12061207
screen_getbytes(int row, int col, char_u *bytes, int *attrp)
@@ -1212,7 +1213,8 @@ screen_getbytes(int row, int col, char_u *bytes, int *attrp)
12121213
return;
12131214

12141215
off = LineOffset[row] + col;
1215-
*attrp = ScreenAttrs[off];
1216+
if (attrp != NULL)
1217+
*attrp = ScreenAttrs[off];
12161218
bytes[0] = ScreenLines[off];
12171219
bytes[1] = NUL;
12181220

src/testdir/test_functions.vim

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3217,6 +3217,31 @@ func Test_screen_functions()
32173217
call assert_equal(-1, screenattr(-1, -1))
32183218
call assert_equal(-1, screenchar(-1, -1))
32193219
call assert_equal([], screenchars(-1, -1))
3220+
3221+
" Run this in a separate Vim instance to avoid messing up.
3222+
let after =<< trim [CODE]
3223+
scriptencoding utf-8
3224+
call setline(1, '')
3225+
redraw
3226+
call assert_equal(0, screenattr(1, 1))
3227+
call assert_equal(char2nr(''), screenchar(1, 1))
3228+
call assert_equal([char2nr('')], screenchars(1, 1))
3229+
call assert_equal('', screenstring(1, 1))
3230+
call writefile(v:errors, 'Xresult')
3231+
qall!
3232+
[CODE]
3233+
3234+
let encodings = ['utf-8', 'cp932', 'cp936', 'cp949', 'cp950']
3235+
if !has('win32')
3236+
let encodings += ['euc-jp']
3237+
endif
3238+
for enc in encodings
3239+
let msg = 'enc=' .. enc
3240+
if RunVim([], after, $'--clean --cmd "set encoding={enc}"')
3241+
call assert_equal([], readfile('Xresult'), msg)
3242+
endif
3243+
call delete('Xresult')
3244+
endfor
32203245
endfunc
32213246

32223247
" Test for getcurpos() and setpos()

src/testdir/test_utf8.vim

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,19 @@ func Test_screenchar_utf8()
135135
call assert_equal("B", screenstring(1, 2))
136136
call assert_equal("C\u0308", screenstring(1, 3))
137137

138+
" 1-cell, with 6 composing characters
139+
set maxcombine=6
140+
call setline(1, ["ABC" .. repeat("\u0308", 6)])
141+
redraw
142+
call assert_equal([0x0041], screenchars(1, 1))
143+
call assert_equal([0x0042], 1->screenchars(2))
144+
" This should not use uninitialized memory
145+
call assert_equal([0x0043] + repeat([0x0308], 6), screenchars(1, 3))
146+
call assert_equal("A", screenstring(1, 1))
147+
call assert_equal("B", screenstring(1, 2))
148+
call assert_equal("C" .. repeat("\u0308", 6), screenstring(1, 3))
149+
set maxcombine&
150+
138151
" 2-cells, with composing characters
139152
let text = "\u3042\u3044\u3046\u3099"
140153
call setline(1, text)

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ static char *(features[]) =
695695

696696
static int included_patches[] =
697697
{ /* Add new patch number below this line */
698+
/**/
699+
1598,
698700
/**/
699701
1597,
700702
/**/

0 commit comments

Comments
 (0)