forked from aniani/vim
patch 8.2.2533: Vim9: cannot use a range with :unlet
Problem: Vim9: cannot use a range with :unlet. Solution: Implement ISN_UNLETRANGE.
This commit is contained in:
@@ -365,3 +365,7 @@ EXTERN char e_variable_nr_type_mismatch_expected_str_but_got_str[]
|
|||||||
INIT(= N_("E1163: Variable %d: type mismatch, expected %s but got %s"));
|
INIT(= N_("E1163: Variable %d: type mismatch, expected %s but got %s"));
|
||||||
EXTERN char e_vim9cmd_must_be_followed_by_command[]
|
EXTERN char e_vim9cmd_must_be_followed_by_command[]
|
||||||
INIT(= N_("E1164: vim9cmd must be followed by a command"));
|
INIT(= N_("E1164: vim9cmd must be followed by a command"));
|
||||||
|
EXTERN char e_cannot_use_range_with_assignment_str[]
|
||||||
|
INIT(= N_("E1165: Cannot use a range with an assignment: %s"));
|
||||||
|
EXTERN char e_cannot_use_range_with_dictionary[]
|
||||||
|
INIT(= N_("E1166: Cannot use a range with a dictionary"));
|
||||||
|
|||||||
10
src/eval.c
10
src/eval.c
@@ -1213,15 +1213,7 @@ get_lval(
|
|||||||
|
|
||||||
lp->ll_dict = NULL;
|
lp->ll_dict = NULL;
|
||||||
lp->ll_list = lp->ll_tv->vval.v_list;
|
lp->ll_list = lp->ll_tv->vval.v_list;
|
||||||
lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
|
lp->ll_li = list_find_index(lp->ll_list, &lp->ll_n1);
|
||||||
if (lp->ll_li == NULL)
|
|
||||||
{
|
|
||||||
if (lp->ll_n1 < 0)
|
|
||||||
{
|
|
||||||
lp->ll_n1 = 0;
|
|
||||||
lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lp->ll_li == NULL)
|
if (lp->ll_li == NULL)
|
||||||
{
|
{
|
||||||
clear_tv(&var2);
|
clear_tv(&var2);
|
||||||
|
|||||||
@@ -1656,27 +1656,9 @@ do_unlet_var(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
else if (lp->ll_range)
|
else if (lp->ll_range)
|
||||||
{
|
{
|
||||||
listitem_T *li;
|
if (list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_name, lp->ll_n1,
|
||||||
listitem_T *ll_li = lp->ll_li;
|
!lp->ll_empty2, lp->ll_n2) == FAIL)
|
||||||
int ll_n1 = lp->ll_n1;
|
|
||||||
|
|
||||||
while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1))
|
|
||||||
{
|
|
||||||
li = ll_li->li_next;
|
|
||||||
if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE))
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
ll_li = li;
|
|
||||||
++ll_n1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete a range of List items.
|
|
||||||
while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1))
|
|
||||||
{
|
|
||||||
li = lp->ll_li->li_next;
|
|
||||||
listitem_remove(lp->ll_list, lp->ll_li);
|
|
||||||
lp->ll_li = li;
|
|
||||||
++lp->ll_n1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1691,6 +1673,43 @@ do_unlet_var(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlet one item or a range of items from a list.
|
||||||
|
* Return OK or FAIL.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
list_unlet_range(
|
||||||
|
list_T *l,
|
||||||
|
listitem_T *li_first,
|
||||||
|
char_u *name,
|
||||||
|
long n1_arg,
|
||||||
|
int has_n2,
|
||||||
|
long n2)
|
||||||
|
{
|
||||||
|
listitem_T *li = li_first;
|
||||||
|
int n1 = n1_arg;
|
||||||
|
|
||||||
|
while (li != NULL && (!has_n2 || n2 >= n1))
|
||||||
|
{
|
||||||
|
if (value_check_lock(li->li_tv.v_lock, name, FALSE))
|
||||||
|
return FAIL;
|
||||||
|
li = li->li_next;
|
||||||
|
++n1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a range of List items.
|
||||||
|
li = li_first;
|
||||||
|
n1 = n1_arg;
|
||||||
|
while (li != NULL && (!has_n2 || n2 >= n1))
|
||||||
|
{
|
||||||
|
listitem_T *next = li->li_next;
|
||||||
|
|
||||||
|
listitem_remove(l, li);
|
||||||
|
li = next;
|
||||||
|
++n1;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* "unlet" a variable. Return OK if it existed, FAIL if not.
|
* "unlet" a variable. Return OK if it existed, FAIL if not.
|
||||||
* When "forceit" is TRUE don't complain if the variable doesn't exist.
|
* When "forceit" is TRUE don't complain if the variable doesn't exist.
|
||||||
|
|||||||
20
src/list.c
20
src/list.c
@@ -530,6 +530,26 @@ list_find_str(list_T *l, long idx)
|
|||||||
return tv_get_string(&li->li_tv);
|
return tv_get_string(&li->li_tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Like list_find() but when a negative index is used that is not found use
|
||||||
|
* zero and set "idx" to zero. Used for first index of a range.
|
||||||
|
*/
|
||||||
|
listitem_T *
|
||||||
|
list_find_index(list_T *l, long *idx)
|
||||||
|
{
|
||||||
|
listitem_T *li = list_find(l, *idx);
|
||||||
|
|
||||||
|
if (li == NULL)
|
||||||
|
{
|
||||||
|
if (*idx < 0)
|
||||||
|
{
|
||||||
|
*idx = 0;
|
||||||
|
li = list_find(l, *idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return li;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Locate "item" list "l" and return its index.
|
* Locate "item" list "l" and return its index.
|
||||||
* Returns -1 when "item" is not in the list.
|
* Returns -1 when "item" is not in the list.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ void list_hashtable_vars(hashtab_T *ht, char *prefix, int empty, int *first);
|
|||||||
void ex_unlet(exarg_T *eap);
|
void ex_unlet(exarg_T *eap);
|
||||||
void ex_lockvar(exarg_T *eap);
|
void ex_lockvar(exarg_T *eap);
|
||||||
void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie);
|
void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie);
|
||||||
|
int list_unlet_range(list_T *l, listitem_T *li_first, char_u *name, long n1_arg, int has_n2, long n2);
|
||||||
int do_unlet(char_u *name, int forceit);
|
int do_unlet(char_u *name, int forceit);
|
||||||
void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
|
void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
|
||||||
void del_menutrans_vars(void);
|
void del_menutrans_vars(void);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ int list_equal(list_T *l1, list_T *l2, int ic, int recursive);
|
|||||||
listitem_T *list_find(list_T *l, long n);
|
listitem_T *list_find(list_T *l, long n);
|
||||||
long list_find_nr(list_T *l, long idx, int *errorp);
|
long list_find_nr(list_T *l, long idx, int *errorp);
|
||||||
char_u *list_find_str(list_T *l, long idx);
|
char_u *list_find_str(list_T *l, long idx);
|
||||||
|
listitem_T *list_find_index(list_T *l, long *idx);
|
||||||
long list_idx_of_item(list_T *l, listitem_T *item);
|
long list_idx_of_item(list_T *l, listitem_T *item);
|
||||||
void list_append(list_T *l, listitem_T *item);
|
void list_append(list_T *l, listitem_T *item);
|
||||||
int list_append_tv(list_T *l, typval_T *tv);
|
int list_append_tv(list_T *l, typval_T *tv);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ let g:existing = 'yes'
|
|||||||
let g:inc_counter = 1
|
let g:inc_counter = 1
|
||||||
let $SOME_ENV_VAR = 'some'
|
let $SOME_ENV_VAR = 'some'
|
||||||
let g:alist = [7]
|
let g:alist = [7]
|
||||||
|
let g:adict = #{a: 1}
|
||||||
let g:astring = 'text'
|
let g:astring = 'text'
|
||||||
|
|
||||||
def Test_assignment_bool()
|
def Test_assignment_bool()
|
||||||
@@ -1414,6 +1415,51 @@ def Test_unlet()
|
|||||||
unlet ll[-1]
|
unlet ll[-1]
|
||||||
assert_equal([1, 3], ll)
|
assert_equal([1, 3], ll)
|
||||||
|
|
||||||
|
ll = [1, 2, 3, 4]
|
||||||
|
unlet ll[0 : 1]
|
||||||
|
assert_equal([3, 4], ll)
|
||||||
|
|
||||||
|
ll = [1, 2, 3, 4]
|
||||||
|
unlet ll[2 : 8]
|
||||||
|
assert_equal([1, 2], ll)
|
||||||
|
|
||||||
|
ll = [1, 2, 3, 4]
|
||||||
|
unlet ll[-2 : -1]
|
||||||
|
assert_equal([1, 2], ll)
|
||||||
|
|
||||||
|
CheckDefFailure([
|
||||||
|
'var ll = [1, 2]',
|
||||||
|
'll[1 : 2] = 7',
|
||||||
|
], 'E1165:', 2)
|
||||||
|
CheckDefFailure([
|
||||||
|
'var dd = {a: 1}',
|
||||||
|
'unlet dd["a" : "a"]',
|
||||||
|
], 'E1166:', 2)
|
||||||
|
CheckDefExecFailure([
|
||||||
|
'unlet g:adict[0 : 1]',
|
||||||
|
], 'E1148:', 1)
|
||||||
|
CheckDefFailure([
|
||||||
|
'var ll = [1, 2]',
|
||||||
|
'unlet ll[0:1]',
|
||||||
|
], 'E1004:', 2)
|
||||||
|
CheckDefFailure([
|
||||||
|
'var ll = [1, 2]',
|
||||||
|
'unlet ll[0 :1]',
|
||||||
|
], 'E1004:', 2)
|
||||||
|
CheckDefFailure([
|
||||||
|
'var ll = [1, 2]',
|
||||||
|
'unlet ll[0: 1]',
|
||||||
|
], 'E1004:', 2)
|
||||||
|
|
||||||
|
CheckDefFailure([
|
||||||
|
'var ll = [1, 2]',
|
||||||
|
'unlet ll["x" : 1]',
|
||||||
|
], 'E1012:', 2)
|
||||||
|
CheckDefFailure([
|
||||||
|
'var ll = [1, 2]',
|
||||||
|
'unlet ll[0 : "x"]',
|
||||||
|
], 'E1012:', 2)
|
||||||
|
|
||||||
# list of dict unlet
|
# list of dict unlet
|
||||||
var dl = [{a: 1, b: 2}, {c: 3}]
|
var dl = [{a: 1, b: 2}, {c: 3}]
|
||||||
unlet dl[0]['b']
|
unlet dl[0]['b']
|
||||||
|
|||||||
@@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2533,
|
||||||
/**/
|
/**/
|
||||||
2532,
|
2532,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ typedef enum {
|
|||||||
ISN_UNLET, // unlet variable isn_arg.unlet.ul_name
|
ISN_UNLET, // unlet variable isn_arg.unlet.ul_name
|
||||||
ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name
|
ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name
|
||||||
ISN_UNLETINDEX, // unlet item of list or dict
|
ISN_UNLETINDEX, // unlet item of list or dict
|
||||||
|
ISN_UNLETRANGE, // unlet items of list
|
||||||
|
|
||||||
ISN_LOCKCONST, // lock constant value
|
ISN_LOCKCONST, // lock constant value
|
||||||
|
|
||||||
|
|||||||
@@ -5865,6 +5865,7 @@ compile_assign_unlet(
|
|||||||
vartype_T dest_type;
|
vartype_T dest_type;
|
||||||
size_t varlen = lhs->lhs_varlen;
|
size_t varlen = lhs->lhs_varlen;
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
|
int range = FALSE;
|
||||||
|
|
||||||
// Compile the "idx" in "var[idx]" or "key" in "var.key".
|
// Compile the "idx" in "var[idx]" or "key" in "var.key".
|
||||||
p = var_start + varlen;
|
p = var_start + varlen;
|
||||||
@@ -5872,6 +5873,27 @@ compile_assign_unlet(
|
|||||||
{
|
{
|
||||||
p = skipwhite(p + 1);
|
p = skipwhite(p + 1);
|
||||||
r = compile_expr0(&p, cctx);
|
r = compile_expr0(&p, cctx);
|
||||||
|
|
||||||
|
if (r == OK && *skipwhite(p) == ':')
|
||||||
|
{
|
||||||
|
// unlet var[idx : idx]
|
||||||
|
if (is_assign)
|
||||||
|
{
|
||||||
|
semsg(_(e_cannot_use_range_with_assignment_str), p);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
range = TRUE;
|
||||||
|
p = skipwhite(p);
|
||||||
|
if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
|
||||||
|
{
|
||||||
|
semsg(_(e_white_space_required_before_and_after_str_at_str),
|
||||||
|
":", p);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
p = skipwhite(p + 1);
|
||||||
|
r = compile_expr0(&p, cctx);
|
||||||
|
}
|
||||||
|
|
||||||
if (r == OK && *skipwhite(p) != ']')
|
if (r == OK && *skipwhite(p) != ']')
|
||||||
{
|
{
|
||||||
// this should not happen
|
// this should not happen
|
||||||
@@ -5897,17 +5919,29 @@ compile_assign_unlet(
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
dest_type = lhs->lhs_type->tt_type;
|
dest_type = lhs->lhs_type->tt_type;
|
||||||
|
if (dest_type == VAR_DICT && range)
|
||||||
|
{
|
||||||
|
emsg(e_cannot_use_range_with_dictionary);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL)
|
if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if (dest_type == VAR_LIST
|
if (dest_type == VAR_LIST)
|
||||||
&& need_type(((type_T **)stack->ga_data)[stack->ga_len - 1],
|
{
|
||||||
|
if (range
|
||||||
|
&& need_type(((type_T **)stack->ga_data)[stack->ga_len - 2],
|
||||||
&t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
|
&t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
if (need_type(((type_T **)stack->ga_data)[stack->ga_len - 1],
|
||||||
|
&t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the dict or list. On the stack we then have:
|
// Load the dict or list. On the stack we then have:
|
||||||
// - value (for assignment, not for :unlet)
|
// - value (for assignment, not for :unlet)
|
||||||
// - index
|
// - index
|
||||||
|
// - for [a : b] second index
|
||||||
// - variable
|
// - variable
|
||||||
if (lhs->lhs_dest == dest_expr)
|
if (lhs->lhs_dest == dest_expr)
|
||||||
{
|
{
|
||||||
@@ -5946,6 +5980,11 @@ compile_assign_unlet(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
isn->isn_arg.vartype = dest_type;
|
isn->isn_arg.vartype = dest_type;
|
||||||
}
|
}
|
||||||
|
else if (range)
|
||||||
|
{
|
||||||
|
if (generate_instr_drop(cctx, ISN_UNLETRANGE, 3) == NULL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (generate_instr_drop(cctx, ISN_UNLETINDEX, 2) == NULL)
|
if (generate_instr_drop(cctx, ISN_UNLETINDEX, 2) == NULL)
|
||||||
@@ -8907,6 +8946,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_TRY:
|
case ISN_TRY:
|
||||||
case ISN_TRYCONT:
|
case ISN_TRYCONT:
|
||||||
case ISN_UNLETINDEX:
|
case ISN_UNLETINDEX:
|
||||||
|
case ISN_UNLETRANGE:
|
||||||
case ISN_UNPACK:
|
case ISN_UNPACK:
|
||||||
// nothing allocated
|
// nothing allocated
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -878,6 +878,21 @@ error_if_locked(int lock, char *error)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Give an error if "tv" is not a number and return FAIL.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
check_for_number(typval_T *tv)
|
||||||
|
{
|
||||||
|
if (tv->v_type != VAR_NUMBER)
|
||||||
|
{
|
||||||
|
semsg(_(e_expected_str_but_got_str),
|
||||||
|
vartype_name(VAR_NUMBER), vartype_name(tv->v_type));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store "tv" in variable "name".
|
* Store "tv" in variable "name".
|
||||||
* This is for s: and g: variables.
|
* This is for s: and g: variables.
|
||||||
@@ -2178,12 +2193,9 @@ call_def_function(
|
|||||||
else if (tv_dest->v_type == VAR_LIST)
|
else if (tv_dest->v_type == VAR_LIST)
|
||||||
{
|
{
|
||||||
// unlet a List item, index must be a number
|
// unlet a List item, index must be a number
|
||||||
if (tv_idx->v_type != VAR_NUMBER)
|
|
||||||
{
|
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
semsg(_(e_expected_str_but_got_str),
|
if (check_for_number(tv_idx) == FAIL)
|
||||||
vartype_name(VAR_NUMBER),
|
{
|
||||||
vartype_name(tv_idx->v_type));
|
|
||||||
status = FAIL;
|
status = FAIL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2219,6 +2231,58 @@ call_def_function(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// unlet range of items in list variable
|
||||||
|
case ISN_UNLETRANGE:
|
||||||
|
{
|
||||||
|
// Stack contains:
|
||||||
|
// -3 index1
|
||||||
|
// -2 index2
|
||||||
|
// -1 dict or list
|
||||||
|
typval_T *tv_idx1 = STACK_TV_BOT(-3);
|
||||||
|
typval_T *tv_idx2 = STACK_TV_BOT(-2);
|
||||||
|
typval_T *tv_dest = STACK_TV_BOT(-1);
|
||||||
|
int status = OK;
|
||||||
|
|
||||||
|
if (tv_dest->v_type == VAR_LIST)
|
||||||
|
{
|
||||||
|
// indexes must be a number
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
|
if (check_for_number(tv_idx1) == FAIL
|
||||||
|
|| check_for_number(tv_idx2) == FAIL)
|
||||||
|
{
|
||||||
|
status = FAIL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list_T *l = tv_dest->vval.v_list;
|
||||||
|
long n1 = (long)tv_idx1->vval.v_number;
|
||||||
|
long n2 = (long)tv_idx2->vval.v_number;
|
||||||
|
listitem_T *li;
|
||||||
|
|
||||||
|
li = list_find_index(l, &n1);
|
||||||
|
if (li == NULL
|
||||||
|
|| list_unlet_range(l, li, NULL, n1,
|
||||||
|
TRUE, n2) == FAIL)
|
||||||
|
status = FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = FAIL;
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
|
semsg(_(e_cannot_index_str),
|
||||||
|
vartype_name(tv_dest->v_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_tv(tv_idx1);
|
||||||
|
clear_tv(tv_idx2);
|
||||||
|
clear_tv(tv_dest);
|
||||||
|
ectx.ec_stack.ga_len -= 3;
|
||||||
|
if (status == FAIL)
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// push constant
|
// push constant
|
||||||
case ISN_PUSHNR:
|
case ISN_PUSHNR:
|
||||||
case ISN_PUSHBOOL:
|
case ISN_PUSHBOOL:
|
||||||
@@ -4151,6 +4215,9 @@ ex_disassemble(exarg_T *eap)
|
|||||||
case ISN_UNLETINDEX:
|
case ISN_UNLETINDEX:
|
||||||
smsg("%4d UNLETINDEX", current);
|
smsg("%4d UNLETINDEX", current);
|
||||||
break;
|
break;
|
||||||
|
case ISN_UNLETRANGE:
|
||||||
|
smsg("%4d UNLETRANGE", current);
|
||||||
|
break;
|
||||||
case ISN_LOCKCONST:
|
case ISN_LOCKCONST:
|
||||||
smsg("%4d LOCKCONST", current);
|
smsg("%4d LOCKCONST", current);
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user