# HG changeset patch # User Bram Moolenaar # Date 1396469830 -7200 # Node ID 9f8fa56f1906f4f634cd602a7a2b4f8631faf526 # Parent fa96ffb832d853f3b8f4dde3baad1a31905f2584 updated for version 7.4.243 Problem: Cannot use setreg() to add text that includes a NUL. Solution: Make setreg() accept a list. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -5368,6 +5368,8 @@ setqflist({list} [, {action}]) *setqf *setreg()* setreg({regname}, {value} [,{options}]) Set the register {regname} to {value}. + {value} may be any value returned by |getreg()|, including + a |List|. If {options} contains "a" or {regname} is upper case, then the value is appended. {options} can also contain a register type specification: @@ -5380,10 +5382,15 @@ setreg({regname}, {value} [,{options}]) in the longest line (counting a as 1 character). If {options} contains no register settings, then the default - is to use character mode unless {value} ends in a . - Setting the '=' register is not possible, but you can use > - :let @= = var_expr -< Returns zero for success, non-zero for failure. + is to use character mode unless {value} ends in a for + string {value} and linewise mode for list {value}. Blockwise + mode is never selected automatically. + Returns zero for success, non-zero for failure. + + *E883* + Note: you may not use |List| containing more then one item to + set search and expression registers. Lists containing no + items act like empty strings. Examples: > :call setreg(v:register, @*) @@ -5391,8 +5398,11 @@ setreg({regname}, {value} [,{options}]) :call setreg('a', "1\n2\n3", 'b5') < This example shows using the functions to save and restore a - register. > - :let var_a = getreg('a', 1) + register (note: you may not reliably restore register value + without using the third argument to |getreg()| as without it + newlines are represented as newlines AND Nul bytes are + represented as newlines as well, see |NL-used-for-Nul|). > + :let var_a = getreg('a', 1, 1) :let var_amode = getregtype('a') .... :call setreg('a', var_a, var_amode) diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -16790,8 +16790,6 @@ f_setreg(argvars, rettv) regname = *strregname; if (regname == 0 || regname == '@') regname = '"'; - else if (regname == '=') - return; if (argvars[2].v_type != VAR_UNKNOWN) { @@ -16822,10 +16820,44 @@ f_setreg(argvars, rettv) } } - strval = get_tv_string_chk(&argvars[1]); - if (strval != NULL) + if (argvars[1].v_type == VAR_LIST) + { + char_u **lstval; + char_u **curval; + int len = argvars[1].vval.v_list->lv_len; + listitem_T *li; + + lstval = (char_u **)alloc(sizeof(char_u *) * (len + 1)); + if (lstval == NULL) + return; + curval = lstval; + + for (li = argvars[1].vval.v_list->lv_first; li != NULL; + li = li->li_next) + { + /* TODO: this may use a static buffer several times. */ + strval = get_tv_string_chk(&li->li_tv); + if (strval == NULL) + { + vim_free(lstval); + return; + } + *curval++ = strval; + } + *curval++ = NULL; + + write_reg_contents_lst(regname, lstval, -1, + append, yank_type, block_len); + vim_free(lstval); + } + else + { + strval = get_tv_string_chk(&argvars[1]); + if (strval == NULL) + return; write_reg_contents_ex(regname, strval, -1, append, yank_type, block_len); + } rettv->vval.v_number = 0; } diff --git a/src/ops.c b/src/ops.c --- a/src/ops.c +++ b/src/ops.c @@ -113,7 +113,7 @@ static char_u *skip_comment __ARGS((char #endif static void block_prep __ARGS((oparg_T *oap, struct block_def *, linenr_T, int)); #if defined(FEAT_CLIPBOARD) || defined(FEAT_EVAL) -static void str_to_reg __ARGS((struct yankreg *y_ptr, int type, char_u *str, long len, long blocklen)); +static void str_to_reg __ARGS((struct yankreg *y_ptr, int type, char_u *str, long len, long blocklen, int str_list)); #endif static int ends_in_white __ARGS((linenr_T lnum)); #ifdef FEAT_COMMENTS @@ -6005,7 +6005,7 @@ clip_yank_selection(type, str, len, cbd) clip_free_selection(cbd); - str_to_reg(y_ptr, type, str, len, 0L); + str_to_reg(y_ptr, type, str, len, 0L, FALSE); } /* @@ -6113,7 +6113,7 @@ dnd_yank_drag_data(str, len) curr = y_current; y_current = &y_regs[TILDE_REGISTER]; free_yank_all(); - str_to_reg(y_current, MCHAR, str, len, 0L); + str_to_reg(y_current, MCHAR, str, len, 0L, FALSE); y_current = curr; } #endif @@ -6308,6 +6308,47 @@ get_reg_contents(regname, flags) return retval; } + static int +init_write_reg(name, old_y_previous, old_y_current, must_append, yank_type) + int name; + struct yankreg **old_y_previous; + struct yankreg **old_y_current; + int must_append; + int *yank_type UNUSED; +{ + if (!valid_yank_reg(name, TRUE)) /* check for valid reg name */ + { + emsg_invreg(name); + return FAIL; + } + + /* Don't want to change the current (unnamed) register */ + *old_y_previous = y_previous; + *old_y_current = y_current; + + get_yank_register(name, TRUE); + if (!y_append && !must_append) + free_yank_all(); + return OK; +} + + static void +finish_write_reg(name, old_y_previous, old_y_current) + int name; + struct yankreg *old_y_previous; + struct yankreg *old_y_current; +{ +# ifdef FEAT_CLIPBOARD + /* Send text of clipboard register to the clipboard. */ + may_set_selection(); +# endif + + /* ':let @" = "val"' should change the meaning of the "" register */ + if (name != '"') + y_previous = old_y_previous; + y_current = old_y_current; +} + /* * Store string "str" in register "name". * "maxlen" is the maximum number of bytes to use, -1 for all bytes. @@ -6328,6 +6369,51 @@ write_reg_contents(name, str, maxlen, mu } void +write_reg_contents_lst(name, strings, maxlen, must_append, yank_type, block_len) + int name; + char_u **strings; + int maxlen UNUSED; + int must_append; + int yank_type; + long block_len; +{ + struct yankreg *old_y_previous, *old_y_current; + + if (name == '/' +#ifdef FEAT_EVAL + || name == '=' +#endif + ) + { + char_u *s; + + if (strings[0] == NULL) + s = (char_u *)""; + else if (strings[1] != NULL) + { + EMSG(_("E883: search pattern and expression register may not " + "contain two or more lines")); + return; + } + else + s = strings[0]; + write_reg_contents_ex(name, s, -1, must_append, yank_type, block_len); + return; + } + + if (name == '_') /* black hole: nothing to do */ + return; + + if (init_write_reg(name, &old_y_previous, &old_y_current, must_append, + &yank_type) == FAIL) + return; + + str_to_reg(y_current, yank_type, (char_u *) strings, -1, block_len, TRUE); + + finish_write_reg(name, old_y_previous, old_y_current); +} + + void write_reg_contents_ex(name, str, maxlen, must_append, yank_type, block_len) int name; char_u *str; @@ -6364,40 +6450,22 @@ write_reg_contents_ex(name, str, maxlen, s = concat_str(get_expr_line_src(), p); vim_free(p); p = s; - } set_expr_line(p); return; } #endif - if (!valid_yank_reg(name, TRUE)) /* check for valid reg name */ - { - emsg_invreg(name); - return; - } - if (name == '_') /* black hole: nothing to do */ return; - /* Don't want to change the current (unnamed) register */ - old_y_previous = y_previous; - old_y_current = y_current; - - get_yank_register(name, TRUE); - if (!y_append && !must_append) - free_yank_all(); - str_to_reg(y_current, yank_type, str, len, block_len); - -# ifdef FEAT_CLIPBOARD - /* Send text of clipboard register to the clipboard. */ - may_set_selection(); -# endif - - /* ':let @" = "val"' should change the meaning of the "" register */ - if (name != '"') - y_previous = old_y_previous; - y_current = old_y_current; + if (init_write_reg(name, &old_y_previous, &old_y_current, must_append, + &yank_type) == FAIL) + return; + + str_to_reg(y_current, yank_type, str, len, block_len, FALSE); + + finish_write_reg(name, old_y_previous, old_y_current); } #endif /* FEAT_EVAL */ @@ -6407,12 +6475,13 @@ write_reg_contents_ex(name, str, maxlen, * is appended. */ static void -str_to_reg(y_ptr, yank_type, str, len, blocklen) +str_to_reg(y_ptr, yank_type, str, len, blocklen, str_list) struct yankreg *y_ptr; /* pointer to yank register */ int yank_type; /* MCHAR, MLINE, MBLOCK, MAUTO */ char_u *str; /* string to put in register */ long len; /* length of string */ long blocklen; /* width of Visual block */ + int str_list; /* TRUE if str is char_u ** */ { int type; /* MCHAR, MLINE or MBLOCK */ int lnum; @@ -6423,6 +6492,7 @@ str_to_reg(y_ptr, yank_type, str, len, b int extraline = 0; /* extra line at the end */ int append = FALSE; /* append to last line in register */ char_u *s; + char_u **ss; char_u **pp; long maxlen; @@ -6430,7 +6500,8 @@ str_to_reg(y_ptr, yank_type, str, len, b y_ptr->y_size = 0; if (yank_type == MAUTO) - type = ((len > 0 && (str[len - 1] == NL || str[len - 1] == CAR)) + type = ((str_list || (len > 0 && (str[len - 1] == NL + || str[len - 1] == CAR))) ? MLINE : MCHAR); else type = yank_type; @@ -6439,18 +6510,26 @@ str_to_reg(y_ptr, yank_type, str, len, b * Count the number of lines within the string */ newlines = 0; - for (i = 0; i < len; i++) - if (str[i] == '\n') + if (str_list) + { + for (ss = (char_u **) str; *ss != NULL; ++ss) ++newlines; - if (type == MCHAR || len == 0 || str[len - 1] != '\n') - { - extraline = 1; - ++newlines; /* count extra newline at the end */ - } - if (y_ptr->y_size > 0 && y_ptr->y_type == MCHAR) - { - append = TRUE; - --newlines; /* uncount newline when appending first line */ + } + else + { + for (i = 0; i < len; i++) + if (str[i] == '\n') + ++newlines; + if (type == MCHAR || len == 0 || str[len - 1] != '\n') + { + extraline = 1; + ++newlines; /* count extra newline at the end */ + } + if (y_ptr->y_size > 0 && y_ptr->y_type == MCHAR) + { + append = TRUE; + --newlines; /* uncount newline when appending first line */ + } } /* @@ -6470,40 +6549,53 @@ str_to_reg(y_ptr, yank_type, str, len, b /* * Find the end of each line and save it into the array. */ - for (start = 0; start < len + extraline; start += i + 1) - { - for (i = start; i < len; ++i) /* find the end of the line */ - if (str[i] == '\n') - break; - i -= start; /* i is now length of line */ - if (i > maxlen) - maxlen = i; - if (append) + if (str_list) + { + for (ss = (char_u **) str; *ss != NULL; ++ss, ++lnum) { - --lnum; - extra = (int)STRLEN(y_ptr->y_array[lnum]); + i = STRLEN(*ss); + pp[lnum] = vim_strnsave(*ss, i); + if (i > maxlen) + maxlen = i; } - else - extra = 0; - s = alloc((unsigned)(i + extra + 1)); - if (s == NULL) - break; - if (extra) - mch_memmove(s, y_ptr->y_array[lnum], (size_t)extra); - if (append) - vim_free(y_ptr->y_array[lnum]); - if (i) - mch_memmove(s + extra, str + start, (size_t)i); - extra += i; - s[extra] = NUL; - y_ptr->y_array[lnum++] = s; - while (--extra >= 0) + } + else + { + for (start = 0; start < len + extraline; start += i + 1) { - if (*s == NUL) - *s = '\n'; /* replace NUL with newline */ - ++s; + for (i = start; i < len; ++i) /* find the end of the line */ + if (str[i] == '\n') + break; + i -= start; /* i is now length of line */ + if (i > maxlen) + maxlen = i; + if (append) + { + --lnum; + extra = (int)STRLEN(y_ptr->y_array[lnum]); + } + else + extra = 0; + s = alloc((unsigned)(i + extra + 1)); + if (s == NULL) + break; + if (extra) + mch_memmove(s, y_ptr->y_array[lnum], (size_t)extra); + if (append) + vim_free(y_ptr->y_array[lnum]); + if (i) + mch_memmove(s + extra, str + start, (size_t)i); + extra += i; + s[extra] = NUL; + y_ptr->y_array[lnum++] = s; + while (--extra >= 0) + { + if (*s == NUL) + *s = '\n'; /* replace NUL with newline */ + ++s; + } + append = FALSE; /* only first line is appended */ } - append = FALSE; /* only first line is appended */ } y_ptr->y_type = type; y_ptr->y_size = lnum; diff --git a/src/proto/ops.pro b/src/proto/ops.pro --- a/src/proto/ops.pro +++ b/src/proto/ops.pro @@ -56,6 +56,7 @@ char_u get_reg_type __ARGS((int regname, char_u *get_reg_contents __ARGS((int regname, int flags)); void write_reg_contents __ARGS((int name, char_u *str, int maxlen, int must_append)); void write_reg_contents_ex __ARGS((int name, char_u *str, int maxlen, int must_append, int yank_type, long block_len)); +void write_reg_contents_lst __ARGS((int name, char_u **strings, int maxlen, int must_append, int yank_type, long block_len)); void clear_oparg __ARGS((oparg_T *oap)); void cursor_pos_info __ARGS((void)); /* vim: set ft=c : */ diff --git a/src/testdir/test_eval.in b/src/testdir/test_eval.in --- a/src/testdir/test_eval.in +++ b/src/testdir/test_eval.in @@ -1,21 +1,150 @@ -Test for various eval features. +Test for various eval features. vim: set ft=vim : + +Note: system clipboard support is not tested. I do not think anybody will thank +me for messing with clipboard. STARTTEST :so small.vim +:set encoding=latin1 +:set noswapfile +:lang C +:fun AppendRegContents(reg) + call append('$', printf('%s: type %s; value: %s (%s), expr: %s (%s)', a:reg, getregtype(a:reg), getreg(a:reg), string(getreg(a:reg, 0, 1)), getreg(a:reg, 1), string(getreg(a:reg, 1, 1)))) +endfun +:command -nargs=? AR :call AppendRegContents() +:fun SetReg(...) + call call('setreg', a:000) + call append('$', printf('{{{2 setreg(%s)', string(a:000)[1:-2])) + call AppendRegContents(a:1) + if a:1 isnot# '=' + execute "silent normal! Go==\n==\e\"".a:1."P" + endif +endfun +:fun ErrExe(str) + call append('$', 'Executing '.a:str) + try + execute a:str + catch + $put =v:exception + endtry +endfun +:fun Test() +$put ='{{{1 let tests' +let @" = 'abc' +AR " +let @" = "abc\n" +AR " +let @" = "abc\" +AR " +let @= = '"abc"' +AR = + +$put ='{{{1 Basic setreg tests' +call SetReg('a', 'abcA', 'c') +call SetReg('b', 'abcB', 'v') +call SetReg('c', 'abcC', 'l') +call SetReg('d', 'abcD', 'V') +call SetReg('e', 'abcE', 'b') +call SetReg('f', 'abcF', "\") +call SetReg('g', 'abcG', 'b10') +call SetReg('h', 'abcH', "\10") +call SetReg('I', 'abcI') + +$put ='{{{1 Appending single lines with setreg()' +call SetReg('A', 'abcAc', 'c') +call SetReg('A', 'abcAl', 'l') +call SetReg('A', 'abcAc2','c') +call SetReg('b', 'abcBc', 'ca') +call SetReg('b', 'abcBb', 'ba') +call SetReg('b', 'abcBc2','ca') +call SetReg('b', 'abcBb2','b50a') + +call SetReg('C', 'abcCl', 'l') +call SetReg('C', 'abcCc', 'c') +call SetReg('D', 'abcDb', 'b') + +call SetReg('E', 'abcEb', 'b') +call SetReg('E', 'abcEl', 'l') +call SetReg('F', 'abcFc', 'c') + +$put ='{{{1 Appending NL with setreg()' +call setreg('a', 'abcA2', 'c') +call setreg('b', 'abcB2', 'v') +call setreg('c', 'abcC2', 'l') +call setreg('d', 'abcD2', 'V') +call setreg('e', 'abcE2', 'b') +call setreg('f', 'abcF2', "\") +call setreg('g', 'abcG2', 'b10') +call setreg('h', 'abcH2', "\10") +call setreg('I', 'abcI2') + +call SetReg('A', "\n") +call SetReg('B', "\n", 'c') +call SetReg('C', "\n") +call SetReg('D', "\n", 'l') +call SetReg('E', "\n") +call SetReg('F', "\n", 'b') + +$put ='{{{1 Setting lists with setreg()' +call SetReg('a', ['abcA3'], 'c') +call SetReg('b', ['abcB3'], 'l') +call SetReg('c', ['abcC3'], 'b') +call SetReg('d', ['abcD3']) + +$put ='{{{1 Appending lists with setreg()' +call SetReg('A', ['abcA3c'], 'c') +call SetReg('b', ['abcB3l'], 'la') +call SetReg('C', ['abcC3b'], 'lb') +call SetReg('D', ['abcD32']) + +call SetReg('A', ['abcA32']) +call SetReg('B', ['abcB3c'], 'c') +call SetReg('C', ['abcC3l'], 'l') +call SetReg('D', ['abcD3b'], 'b') + +$put ='{{{1 Appending lists with NL with setreg()' +call SetReg('A', ["\n", 'abcA3l2'], 'l') +call SetReg('B', ["\n", 'abcB3c2'], 'c') +call SetReg('C', ["\n", 'abcC3b2'], 'b') +call SetReg('D', ["\n", 'abcD3b50'],'b50') + +$put ='{{{1 Setting lists with NLs with setreg()' +call SetReg('a', ['abcA4-0', "\n", "abcA4-2\n", "\nabcA4-3", "abcA4-4\nabcA4-4-2"]) +call SetReg('b', ['abcB4c-0', "\n", "abcB4c-2\n", "\nabcB4c-3", "abcB4c-4\nabcB4c-4-2"], 'c') +call SetReg('c', ['abcC4l-0', "\n", "abcC4l-2\n", "\nabcC4l-3", "abcC4l-4\nabcC4l-4-2"], 'l') +call SetReg('d', ['abcD4b-0', "\n", "abcD4b-2\n", "\nabcD4b-3", "abcD4b-4\nabcD4b-4-2"], 'b') +call SetReg('e', ['abcE4b10-0', "\n", "abcE4b10-2\n", "\nabcE4b10-3", "abcE4b10-4\nabcE4b10-4-2"], 'b10') + +$put ='{{{1 Search and expressions' +call SetReg('/', ['abc/']) +call SetReg('/', ["abc/\n"]) +call SetReg('=', ['"abc/"']) +call SetReg('=', ["\"abc/\n\""]) + +$put ='{{{1 Errors' +call ErrExe('call setreg()') +call ErrExe('call setreg(1)') +call ErrExe('call setreg(1, 2, 3, 4)') +call ErrExe('call setreg([], 2)') +call ErrExe('call setreg(1, {})') +call ErrExe('call setreg(1, 2, [])') +call ErrExe('call setreg("/", [1, 2])') +call ErrExe('call setreg("=", [1, 2])') +call ErrExe('call setreg(1, ["", "", [], ""])') +endfun :" -:" test getreg() -/^one -"ay3j:$put =string(getreg('a')) -:$put =string(getreg('a', 1, 1)) +:call Test() :" -:/^result/,$w! test.out -:qa! +:delfunction SetReg +:delfunction AppendRegContents +:delfunction ErrExe +:delfunction Test +:delcommand AR +:call garbagecollect(1) +:" +:/^start:/+1,$wq! test.out +:" vim: et ts=4 isk-=\: fmr=???,??? +:call getchar() ENDTEST -one -two -three -four -five - -result +start: diff --git a/src/testdir/test_eval.ok b/src/testdir/test_eval.ok index 8e6c987bd0b6af1ec712dc5948826a87d93b13ae..7fe5f1bd1b2745600d74f4bb8a86e7fa1391f607 GIT binary patch literal 10307 zc%02zOK;ma5awAxuQ?OGqybXcmYi*yg%7qRCjp9Hy67e7L2{JDfMN%>(rys!e_y_4 zNJ>N5Np?3IpcOTonQutWOO~FVo`$?CYhIVzdK>jldHr}_@}Ix+pLz9Ap7Oj{@b{O8 z;&S-o=$My(-ftR26#DK8F;a@{5_<<6?|eaz9*y@ivnOtQlB@<&@JTLrns1j2zAfub zd1KZs=&_-^Kg;n`AU@EUa1wcUoOpT_g z58#pkVA=p!7=W3)Rsvu_?ez@ybmk*?bO|Ac^6Ft+8H`k3zYB~j3L^zwsgL2&4My6+ zC=Es?uZ6%U4aUL91<8!A%*Sxauv1wB;o2a~<@G=yT$2cMqiXIWxMV<>HxO0Ci46T^OhffpI~3 z?}bry;UlV|K{e^tq=<*MBBU(5OyaF2W|CJ?C$2-OK0s)_{y zQ}g>lOEO^iD#ADbT|taZDF4^8ijR5vwJ2dl2d{|}huDf3yCd-)TcWWvJAr6b1?}8R z*562NxSk9pMtz=nFf}S3NQ7FzPm&}9rb|T#VXHymVjrBe!4;!5oHTC&R+qkG44o}q_W8sFn@9C25X zzY8-c$CxW-bUju)w)&z}qdbS%uv#lZD?1=B=F1u7OMsv_+c2nuG;6mIY%y4==nYF# z=q?H<{V5rYDN{_?OrTVw+X!-jF{wGiwD1kdpanrgGSZNYDJE<=P|9yf!d%#)7(=p< zA?QRIzAGF0}Gb!7^`rz0QxgfmgCkoOf`Vnf+?A|QP)(Z$VMt_&tifSa71*oV0MWa zrVIj!nucr=N3E5mxU$NLO~3WG8dCz0nuZ4f0x*Nc(Oz$rWds&A4Sq~u&2e#sx&owBvC$8z zXm;8FY0Q9+!h+m%v-2P)_8=E{l5~TfFwJJq&9u}>^WmkN)1GytY2hQU^qJ=!hpzdY z6E+=GQnkFGlhwL~CXua*Iod*-RXCaL;5Xktm}a!zVA_qOXgW+Mr}ZFHgax^s_lz&7 z)K6zVg(5f{$1yEO61SYIlht~gCXo+(=GYZKUCO{k^u?Ah*HvKDEOyV8g>ne~rZ04H zG_GM|fv%D0Oq!c=DHt^$1fMC2)fF96Pldg+!bC}zUGO-4v2DBSc`#~r`p=bxf}KRh zcl8Wey^XgOn0r$)wyc9{cFIj*)a<&SDT=K)M4$9`eJwgN?d~4$QWtaOhgFQ{wO^<( zX5@A_KC}#o(iucA*BS~h78#yEpfT&`@$8!P3yqsOsTgLDPr%aw*gRPTUfB7>FL zn2FKnv7UVFj9F&x$yN^h`1|N3RmPJQ%Y;6?A~L#)P3-z1hkG9J@!Vw`EeAF9NzA=(u zjgfbWGcy7r98LILrc`WFs#1x=gb~WHlX{IkUUq*Ad}#Ni5`8vWJVM!s5G3E^>YO_& zd}}1ZS|jfgXJ!OMIGw_I3w$vA?9~ye+GKg96o&~T)Ikw=jX_?$c>{Q?OHzwpcub*D z+Qc2`LaM8&{YG5>jx{QV1K&DLvi9lw#G4)22|P9Rq8Q(-`!9+OjNUH*aBdP7Pz^t~ z{WE5IKj!^UOu`@RwLUynP^J&osB&g}MbK#Eo5d~9S66DC<#xMVuNt4F^x^3H61-0p zsJ>ZZ-`_Ep3|9BYA?+-z9@3WLaRIyugkTMfvBHoaXcXc735rw^aBnSn${*yn8~lrCn0dbCIsaB~1R-qQ(_al8L(q{fl^OZwaX(FBotHmeX;81<6I!G) zU(=^giQEnqr1yDUmzz~1Sh)}v80AK6=_6l>V_U3Ob-rBj`q!F^ZM|D>%oi;o?D5-S S3)IV=fS3Q|!S;Il@8sXJc?iD% diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -735,6 +735,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 243, +/**/ 242, /**/ 241,