To: vim_dev@googlegroups.com Subject: Patch 8.2.1491 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1491 Problem: Vim9: crash when compiling heredoc lines start with comment. Solution: Skip over NULL pointers. Do not remove comment and empty lines when fetching function lines. (closes #6743) Files: src/vim9compile.c, src/scriptfile.c, src/proto/scriptfile.pro, src/structs.h, src/ex_docmd.c, src/proto/ex_docmd.pro, src/ex_cmds.h, src/autocmd.c, src/proto/autocmd.pro, src/ex_getln.c, src/proto/ex_getln.pro, src/userfunc.c, src/proto/userfunc.pro, src/evalfunc.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.1490/src/vim9compile.c 2020-08-19 22:02:38.195375849 +0200 --- src/vim9compile.c 2020-08-20 14:46:15.223927297 +0200 *************** *** 4197,4214 **** int c UNUSED, void *cookie, int indent UNUSED, ! int do_concat UNUSED) { cctx_T *cctx = (cctx_T *)cookie; ! if (cctx->ctx_lnum == cctx->ctx_ufunc->uf_lines.ga_len) { ! iemsg("Heredoc got to end"); ! return NULL; } - ++cctx->ctx_lnum; - return vim_strsave(((char_u **)cctx->ctx_ufunc->uf_lines.ga_data) - [cctx->ctx_lnum]); } /* --- 4197,4217 ---- int c UNUSED, void *cookie, int indent UNUSED, ! getline_opt_T options UNUSED) { cctx_T *cctx = (cctx_T *)cookie; + char_u *p; ! for (;;) { ! if (cctx->ctx_lnum == cctx->ctx_ufunc->uf_lines.ga_len) ! return NULL; ! ++cctx->ctx_lnum; ! p = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum]; ! // Comment lines result in NULL pointers, skip them. ! if (p != NULL) ! return vim_strsave(p); } } /* *** ../vim-8.2.1490/src/scriptfile.c 2020-08-18 22:31:41.664654206 +0200 --- src/scriptfile.c 2020-08-20 14:35:05.494505500 +0200 *************** *** 1604,1610 **** #endif linenr_T ! get_sourced_lnum(char_u *(*fgetline)(int, void *, int, int), void *cookie) { return fgetline == getsourceline ? ((struct source_cookie *)cookie)->sourcing_lnum --- 1604,1612 ---- #endif linenr_T ! get_sourced_lnum( ! char_u *(*fgetline)(int, void *, int, getline_opt_T), ! void *cookie) { return fgetline == getsourceline ? ((struct source_cookie *)cookie)->sourcing_lnum *************** *** 1724,1730 **** * Return NULL for end-of-file or some error. */ char_u * ! getsourceline(int c UNUSED, void *cookie, int indent UNUSED, int do_concat) { struct source_cookie *sp = (struct source_cookie *)cookie; char_u *line; --- 1726,1736 ---- * Return NULL for end-of-file or some error. */ char_u * ! getsourceline( ! int c UNUSED, ! void *cookie, ! int indent UNUSED, ! getline_opt_T options) { struct source_cookie *sp = (struct source_cookie *)cookie; char_u *line; *************** *** 1765,1771 **** // Only concatenate lines starting with a \ when 'cpoptions' doesn't // contain the 'C' flag. ! if (line != NULL && do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) { // compensate for the one line read-ahead --sp->sourcing_lnum; --- 1771,1778 ---- // Only concatenate lines starting with a \ when 'cpoptions' doesn't // contain the 'C' flag. ! if (line != NULL && options != GETLINE_NONE ! && vim_strchr(p_cpo, CPO_CONCAT) == NULL) { // compensate for the one line read-ahead --sp->sourcing_lnum; *************** *** 1781,1787 **** || (p[0] == '"' && p[1] == '\\' && p[2] == ' ') #ifdef FEAT_EVAL || (in_vim9script() ! && (*p == NUL || vim9_comment_start(p))) #endif )) { --- 1788,1795 ---- || (p[0] == '"' && p[1] == '\\' && p[2] == ' ') #ifdef FEAT_EVAL || (in_vim9script() ! && options == GETLINE_CONCAT_ALL ! && (*p == NUL || vim9_comment_start(p))) #endif )) { *************** *** 1814,1820 **** else if (!(p[0] == '"' && p[1] == '\\' && p[2] == ' ') #ifdef FEAT_EVAL && !(in_vim9script() ! && (*p == NUL || vim9_comment_start(p))) #endif ) break; --- 1822,1829 ---- else if (!(p[0] == '"' && p[1] == '\\' && p[2] == ' ') #ifdef FEAT_EVAL && !(in_vim9script() ! && options == GETLINE_CONCAT_ALL ! && (*p == NUL || vim9_comment_start(p))) #endif ) break; *************** *** 1968,1974 **** */ int source_finished( ! char_u *(*fgetline)(int, void *, int, int), void *cookie) { return (getline_equal(fgetline, cookie, getsourceline) --- 1977,1983 ---- */ int source_finished( ! char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie) { return (getline_equal(fgetline, cookie, getsourceline) *** ../vim-8.2.1490/src/proto/scriptfile.pro 2020-07-26 15:36:12.963172976 +0200 --- src/proto/scriptfile.pro 2020-08-20 14:35:10.910481828 +0200 *************** *** 29,41 **** char_u *get_scriptname(scid_T id); void free_scriptnames(void); void free_autoload_scriptnames(void); ! linenr_T get_sourced_lnum(char_u *(*fgetline)(int, void *, int, int), void *cookie); ! char_u *getsourceline(int c, void *cookie, int indent, int do_concat); void ex_scriptencoding(exarg_T *eap); void ex_scriptversion(exarg_T *eap); void ex_finish(exarg_T *eap); void do_finish(exarg_T *eap, int reanimate); ! int source_finished(char_u *(*fgetline)(int, void *, int, int), void *cookie); char_u *autoload_name(char_u *name); int script_autoload(char_u *name, int reload); /* vim: set ft=c : */ --- 29,41 ---- char_u *get_scriptname(scid_T id); void free_scriptnames(void); void free_autoload_scriptnames(void); ! linenr_T get_sourced_lnum(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); ! char_u *getsourceline(int c, void *cookie, int indent, getline_opt_T options); void ex_scriptencoding(exarg_T *eap); void ex_scriptversion(exarg_T *eap); void ex_finish(exarg_T *eap); void do_finish(exarg_T *eap, int reanimate); ! int source_finished(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); char_u *autoload_name(char_u *name); int script_autoload(char_u *name, int reload); /* vim: set ft=c : */ *** ../vim-8.2.1490/src/structs.h 2020-08-11 21:58:12.585968185 +0200 --- src/structs.h 2020-08-20 14:36:36.562116026 +0200 *************** *** 1761,1766 **** --- 1761,1773 ---- # endif } scriptitem_T; + // type of getline() last argument + typedef enum { + GETLINE_NONE, // do not concatenate any lines + GETLINE_CONCAT_CONT, // concatenate continuation lines + GETLINE_CONCAT_ALL // concatenate continuation and Vim9 # comment lines + } getline_opt_T; + // Struct passed through eval() functions. // See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE. typedef struct { *************** *** 1768,1774 **** int eval_break_count; // nr of line breaks consumed // copied from exarg_T when "getline" is "getsourceline". Can be NULL. ! char_u *(*eval_getline)(int, void *, int, int); void *eval_cookie; // argument for eval_getline() // used when compiling a :def function, NULL otherwise --- 1775,1781 ---- int eval_break_count; // nr of line breaks consumed // copied from exarg_T when "getline" is "getsourceline". Can be NULL. ! char_u *(*eval_getline)(int, void *, int, getline_opt_T); void *eval_cookie; // argument for eval_getline() // used when compiling a :def function, NULL otherwise *** ../vim-8.2.1490/src/ex_docmd.c 2020-08-17 19:34:06.961427079 +0200 --- src/ex_docmd.c 2020-08-20 14:17:19.866484340 +0200 *************** *** 612,618 **** int do_cmdline( char_u *cmdline, ! char_u *(*fgetline)(int, void *, int, int), void *cookie, // argument for fgetline() int flags) { --- 612,618 ---- int do_cmdline( char_u *cmdline, ! char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie, // argument for fgetline() int flags) { *************** *** 638,644 **** msglist_T *private_msg_list; // "fgetline" and "cookie" passed to do_one_cmd() ! char_u *(*cmd_getline)(int, void *, int, int); void *cmd_cookie; struct loop_cookie cmd_loop_cookie; void *real_cookie; --- 638,644 ---- msglist_T *private_msg_list; // "fgetline" and "cookie" passed to do_one_cmd() ! char_u *(*cmd_getline)(int, void *, int, getline_opt_T); void *cmd_cookie; struct loop_cookie cmd_loop_cookie; void *real_cookie; *************** *** 1482,1490 **** */ int getline_equal( ! char_u *(*fgetline)(int, void *, int, int), void *cookie UNUSED, // argument for fgetline() ! char_u *(*func)(int, void *, int, int)) { #ifdef FEAT_EVAL char_u *(*gp)(int, void *, int, int); --- 1482,1490 ---- */ int getline_equal( ! char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie UNUSED, // argument for fgetline() ! char_u *(*func)(int, void *, int, getline_opt_T)) { #ifdef FEAT_EVAL char_u *(*gp)(int, void *, int, int); *************** *** 1512,1518 **** */ void * getline_cookie( ! char_u *(*fgetline)(int, void *, int, int) UNUSED, void *cookie) // argument for fgetline() { #ifdef FEAT_EVAL --- 1512,1518 ---- */ void * getline_cookie( ! char_u *(*fgetline)(int, void *, int, getline_opt_T) UNUSED, void *cookie) // argument for fgetline() { #ifdef FEAT_EVAL *************** *** 1541,1547 **** */ char_u * getline_peek( ! char_u *(*fgetline)(int, void *, int, int) UNUSED, void *cookie) // argument for fgetline() { char_u *(*gp)(int, void *, int, int); --- 1541,1547 ---- */ char_u * getline_peek( ! char_u *(*fgetline)(int, void *, int, getline_opt_T) UNUSED, void *cookie) // argument for fgetline() { char_u *(*gp)(int, void *, int, int); *** ../vim-8.2.1490/src/proto/ex_docmd.pro 2020-07-23 16:36:59.828375424 +0200 --- src/proto/ex_docmd.pro 2020-08-20 14:17:22.358477236 +0200 *************** *** 1,10 **** /* ex_docmd.c */ void do_exmode(int improved); int do_cmdline_cmd(char_u *cmd); ! int do_cmdline(char_u *cmdline, char_u *(*fgetline)(int, void *, int, int), void *cookie, int flags); ! int getline_equal(char_u *(*fgetline)(int, void *, int, int), void *cookie, char_u *(*func)(int, void *, int, int)); ! void *getline_cookie(char_u *(*fgetline)(int, void *, int, int), void *cookie); ! char_u *getline_peek(char_u *(*fgetline)(int, void *, int, int), void *cookie); char *ex_errmsg(char *msg, char_u *arg); int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only); void undo_cmdmod(exarg_T *eap, int save_msg_scroll); --- 1,10 ---- /* ex_docmd.c */ void do_exmode(int improved); int do_cmdline_cmd(char_u *cmd); ! int do_cmdline(char_u *cmdline, char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie, int flags); ! int getline_equal(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie, char_u *(*func)(int, void *, int, getline_opt_T)); ! void *getline_cookie(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); ! char_u *getline_peek(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); char *ex_errmsg(char *msg, char_u *arg); int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only); void undo_cmdmod(exarg_T *eap, int save_msg_scroll); *** ../vim-8.2.1490/src/ex_cmds.h 2020-08-12 21:58:09.000049823 +0200 --- src/ex_cmds.h 2020-08-20 14:09:34.743809352 +0200 *************** *** 1867,1873 **** int bad_char; // BAD_KEEP, BAD_DROP or replacement byte int useridx; // user command index char *errmsg; // returned error message ! char_u *(*getline)(int, void *, int, int); void *cookie; // argument for getline() #ifdef FEAT_EVAL cstack_T *cstack; // condition stack for ":if" etc. --- 1867,1873 ---- int bad_char; // BAD_KEEP, BAD_DROP or replacement byte int useridx; // user command index char *errmsg; // returned error message ! char_u *(*getline)(int, void *, int, getline_opt_T); void *cookie; // argument for getline() #ifdef FEAT_EVAL cstack_T *cstack; // condition stack for ":if" etc. *** ../vim-8.2.1490/src/autocmd.c 2020-06-12 22:15:26.617016573 +0200 --- src/autocmd.c 2020-08-20 14:12:02.163388925 +0200 *************** *** 2310,2316 **** * Returns allocated string, or NULL for end of autocommands. */ char_u * ! getnextac(int c UNUSED, void *cookie, int indent UNUSED, int do_concat UNUSED) { AutoPatCmd *acp = (AutoPatCmd *)cookie; char_u *retval; --- 2310,2320 ---- * Returns allocated string, or NULL for end of autocommands. */ char_u * ! getnextac( ! int c UNUSED, ! void *cookie, ! int indent UNUSED, ! getline_opt_T options UNUSED) { AutoPatCmd *acp = (AutoPatCmd *)cookie; char_u *retval; *** ../vim-8.2.1490/src/proto/autocmd.pro 2020-06-12 22:15:26.617016573 +0200 --- src/proto/autocmd.pro 2020-08-20 14:12:25.027323779 +0200 *************** *** 28,34 **** void block_autocmds(void); void unblock_autocmds(void); int is_autocmd_blocked(void); ! char_u *getnextac(int c, void *cookie, int indent, int do_concat); int has_autocmd(event_T event, char_u *sfname, buf_T *buf); char_u *get_augroup_name(expand_T *xp, int idx); char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd); --- 28,34 ---- void block_autocmds(void); void unblock_autocmds(void); int is_autocmd_blocked(void); ! char_u *getnextac(int c, void *cookie, int indent, getline_opt_T options); int has_autocmd(event_T event, char_u *sfname, buf_T *buf); char_u *get_augroup_name(expand_T *xp, int idx); char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd); *** ../vim-8.2.1490/src/ex_getln.c 2020-06-07 15:46:08.314414830 +0200 --- src/ex_getln.c 2020-08-20 14:13:10.155195219 +0200 *************** *** 2705,2716 **** int c, // normally ':', NUL for ":append" void *cookie UNUSED, int indent, // indent for inside conditionals ! int do_concat) { // When executing a register, remove ':' that's in front of each line. if (exec_from_reg && vpeekc() == ':') (void)vgetc(); ! return getcmdline(c, 1L, indent, do_concat); } /* --- 2705,2716 ---- int c, // normally ':', NUL for ":append" void *cookie UNUSED, int indent, // indent for inside conditionals ! getline_opt_T options) { // When executing a register, remove ':' that's in front of each line. if (exec_from_reg && vpeekc() == ':') (void)vgetc(); ! return getcmdline(c, 1L, indent, options); } /* *************** *** 2725,2731 **** // :s prompt void *cookie UNUSED, int indent, // indent for inside conditionals ! int do_concat UNUSED) { garray_T line_ga; char_u *pend; --- 2725,2731 ---- // :s prompt void *cookie UNUSED, int indent, // indent for inside conditionals ! getline_opt_T options UNUSED) { garray_T line_ga; char_u *pend; *** ../vim-8.2.1490/src/proto/ex_getln.pro 2020-04-30 22:29:36.626024141 +0200 --- src/proto/ex_getln.pro 2020-08-20 14:13:40.111109877 +0200 *************** *** 9,16 **** int text_locked(void); int curbuf_locked(void); int allbuf_locked(void); ! char_u *getexline(int c, void *cookie, int indent, int do_concat); ! char_u *getexmodeline(int promptc, void *cookie, int indent, int do_concat); int cmdline_overstrike(void); int cmdline_at_end(void); colnr_T cmdline_getvcol_cursor(void); --- 9,16 ---- int text_locked(void); int curbuf_locked(void); int allbuf_locked(void); ! char_u *getexline(int c, void *cookie, int indent, getline_opt_T options); ! char_u *getexmodeline(int promptc, void *cookie, int indent, getline_opt_T options); int cmdline_overstrike(void); int cmdline_at_end(void); colnr_T cmdline_getvcol_cursor(void); *** ../vim-8.2.1490/src/userfunc.c 2020-08-15 16:33:24.501747305 +0200 --- src/userfunc.c 2020-08-20 14:42:48.976666092 +0200 *************** *** 2651,2657 **** static int func_nr = 0; // number for nameless function int paren; hashitem_T *hi; ! int do_concat = TRUE; linenr_T sourcing_lnum_off; linenr_T sourcing_lnum_top; int is_heredoc = FALSE; --- 2651,2657 ---- static int func_nr = 0; // number for nameless function int paren; hashitem_T *hi; ! getline_opt_T getline_options = GETLINE_CONCAT_CONT; linenr_T sourcing_lnum_off; linenr_T sourcing_lnum_top; int is_heredoc = FALSE; *************** *** 3008,3016 **** { vim_free(line_to_free); if (eap->getline == NULL) ! theline = getcmdline(':', 0L, indent, do_concat); else ! theline = eap->getline(':', eap->cookie, indent, do_concat); line_to_free = theline; } if (KeyTyped) --- 3008,3017 ---- { vim_free(line_to_free); if (eap->getline == NULL) ! theline = getcmdline(':', 0L, indent, getline_options); else ! theline = eap->getline(':', eap->cookie, indent, ! getline_options); line_to_free = theline; } if (KeyTyped) *************** *** 3053,3059 **** { VIM_CLEAR(skip_until); VIM_CLEAR(heredoc_trimmed); ! do_concat = TRUE; is_heredoc = FALSE; } } --- 3054,3060 ---- { VIM_CLEAR(skip_until); VIM_CLEAR(heredoc_trimmed); ! getline_options = GETLINE_CONCAT_CONT; is_heredoc = FALSE; } } *************** *** 3178,3184 **** skip_until = vim_strsave((char_u *)"."); else skip_until = vim_strnsave(p, skiptowhite(p) - p); ! do_concat = FALSE; is_heredoc = TRUE; } --- 3179,3185 ---- skip_until = vim_strsave((char_u *)"."); else skip_until = vim_strnsave(p, skiptowhite(p) - p); ! getline_options = GETLINE_NONE; is_heredoc = TRUE; } *************** *** 3205,3211 **** skipwhite(theline) - theline); } skip_until = vim_strnsave(p, skiptowhite(p) - p); ! do_concat = FALSE; is_heredoc = TRUE; } } --- 3206,3212 ---- skipwhite(theline) - theline); } skip_until = vim_strnsave(p, skiptowhite(p) - p); ! getline_options = GETLINE_NONE; is_heredoc = TRUE; } } *************** *** 4249,4255 **** int c UNUSED, void *cookie, int indent UNUSED, ! int do_concat UNUSED) { funccall_T *fcp = (funccall_T *)cookie; ufunc_T *fp = fcp->func; --- 4250,4256 ---- int c UNUSED, void *cookie, int indent UNUSED, ! getline_opt_T options UNUSED) { funccall_T *fcp = (funccall_T *)cookie; ufunc_T *fp = fcp->func; *** ../vim-8.2.1490/src/proto/userfunc.pro 2020-07-31 23:47:08.017231395 +0200 --- src/proto/userfunc.pro 2020-08-20 14:35:49.634314497 +0200 *************** *** 46,52 **** int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv); void discard_pending_return(void *rettv); char_u *get_return_cmd(void *rettv); ! char_u *get_func_line(int c, void *cookie, int indent, int do_concat); int func_has_ended(void *cookie); int func_has_abort(void *cookie); dict_T *make_partial(dict_T *selfdict_in, typval_T *rettv); --- 46,52 ---- int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv); void discard_pending_return(void *rettv); char_u *get_return_cmd(void *rettv); ! char_u *get_func_line(int c, void *cookie, int indent, getline_opt_T options); int func_has_ended(void *cookie); int func_has_abort(void *cookie); dict_T *make_partial(dict_T *selfdict_in, typval_T *rettv); *** ../vim-8.2.1490/src/evalfunc.c 2020-08-18 13:41:47.215350419 +0200 --- src/evalfunc.c 2020-08-20 14:32:17.247278655 +0200 *************** *** 2198,2210 **** * Called by do_cmdline() to get the next line. * Returns allocated string, or NULL for end of function. */ - static char_u * get_list_line( int c UNUSED, void *cookie, int indent UNUSED, ! int do_concat UNUSED) { listitem_T **p = (listitem_T **)cookie; listitem_T *item = *p; --- 2198,2209 ---- * Called by do_cmdline() to get the next line. * Returns allocated string, or NULL for end of function. */ static char_u * get_list_line( int c UNUSED, void *cookie, int indent UNUSED, ! getline_opt_T options UNUSED) { listitem_T **p = (listitem_T **)cookie; listitem_T *item = *p; *** ../vim-8.2.1490/src/testdir/test_vim9_script.vim 2020-08-19 22:02:38.199375840 +0200 --- src/testdir/test_vim9_script.vim 2020-08-20 14:50:06.915129527 +0200 *************** *** 1141,1146 **** --- 1141,1157 ---- assert_equal(['one', 'two', 'three'], mylist) END CheckScriptSuccess(lines) + + # check all lines from heredoc are kept + lines =<< trim END + # comment 1 + two + # comment 3 + + five + # comment 6 + END + assert_equal(['# comment 1', 'two', '# comment 3', '', 'five', '# comment 6'], lines) enddef if has('channel') *** ../vim-8.2.1490/src/version.c 2020-08-19 22:02:38.199375840 +0200 --- src/version.c 2020-08-20 13:27:02.075256127 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1491, /**/ -- hundred-and-one symptoms of being an internet addict: 245. You use Real Audio to listen to a radio station from a distant city rather than turn on your stereo system. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///