To: vim_dev@googlegroups.com Subject: Patch 7.3.1191 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.3.1191 Problem: Backreference to previous line doesn't work. (Lech Lorens) Solution: Implement looking in another line. Files: src/regexp.c, src/regexp_nfa.c, src/testdir/test64.in, src/testdir/test64.ok *** ../vim-7.3.1190/src/regexp.c 2013-06-08 18:19:39.000000000 +0200 --- src/regexp.c 2013-06-14 20:23:33.000000000 +0200 *************** *** 3519,3524 **** --- 3519,3525 ---- *(pp) = (savep)->se_u.ptr; } static int re_num_cmp __ARGS((long_u val, char_u *scan)); + static int match_with_backref __ARGS((linenr_T start_lnum, colnr_T start_col, linenr_T end_lnum, colnr_T end_col, int *bytelen)); static int regmatch __ARGS((char_u *prog)); static int regrepeat __ARGS((char_u *p, long maxcount)); *************** *** 4979,4987 **** case BACKREF + 9: { int len; - linenr_T clnum; - colnr_T ccol; - char_u *p; no = op - BACKREF; cleanup_subexpr(); --- 4980,4985 ---- *************** *** 5023,5089 **** { /* Messy situation: Need to compare between two * lines. */ ! ccol = reg_startpos[no].col; ! clnum = reg_startpos[no].lnum; ! for (;;) ! { ! /* Since getting one line may invalidate ! * the other, need to make copy. Slow! */ ! if (regline != reg_tofree) ! { ! len = (int)STRLEN(regline); ! if (reg_tofree == NULL ! || len >= (int)reg_tofreelen) ! { ! len += 50; /* get some extra */ ! vim_free(reg_tofree); ! reg_tofree = alloc(len); ! if (reg_tofree == NULL) ! { ! status = RA_FAIL; /* outof memory!*/ ! break; ! } ! reg_tofreelen = len; ! } ! STRCPY(reg_tofree, regline); ! reginput = reg_tofree ! + (reginput - regline); ! regline = reg_tofree; ! } ! ! /* Get the line to compare with. */ ! p = reg_getline(clnum); ! if (clnum == reg_endpos[no].lnum) ! len = reg_endpos[no].col - ccol; ! else ! len = (int)STRLEN(p + ccol); ! ! if (cstrncmp(p + ccol, reginput, &len) != 0) ! { ! status = RA_NOMATCH; /* doesn't match */ ! break; ! } ! if (clnum == reg_endpos[no].lnum) ! break; /* match and at end! */ ! if (reglnum >= reg_maxline) ! { ! status = RA_NOMATCH; /* text too short */ ! break; ! } ! ! /* Advance to next line. */ ! reg_nextline(); ! ++clnum; ! ccol = 0; ! if (got_int) ! { ! status = RA_FAIL; ! break; ! } ! } ! ! /* found a match! Note that regline may now point ! * to a copy of the line, that should not matter. */ } } } --- 5021,5032 ---- { /* Messy situation: Need to compare between two * lines. */ ! status = match_with_backref( ! reg_startpos[no].lnum, ! reg_startpos[no].col, ! reg_endpos[no].lnum, ! reg_endpos[no].col, ! NULL); } } } *************** *** 6505,6510 **** --- 6448,6522 ---- return val == n; } + /* + * Check whether a backreference matches. + * Returns RA_FAIL, RA_NOMATCH or RA_MATCH. + * If "bytelen" is not NULL, it is set to the bytelength of the whole match. + */ + static int + match_with_backref(start_lnum, start_col, end_lnum, end_col, bytelen) + linenr_T start_lnum; + colnr_T start_col; + linenr_T end_lnum; + colnr_T end_col; + int *bytelen; + { + linenr_T clnum = start_lnum; + colnr_T ccol = start_col; + int len; + char_u *p; + + if (bytelen != NULL) + *bytelen = 0; + for (;;) + { + /* Since getting one line may invalidate the other, need to make copy. + * Slow! */ + if (regline != reg_tofree) + { + len = (int)STRLEN(regline); + if (reg_tofree == NULL || len >= (int)reg_tofreelen) + { + len += 50; /* get some extra */ + vim_free(reg_tofree); + reg_tofree = alloc(len); + if (reg_tofree == NULL) + return RA_FAIL; /* out of memory!*/ + reg_tofreelen = len; + } + STRCPY(reg_tofree, regline); + reginput = reg_tofree + (reginput - regline); + regline = reg_tofree; + } + + /* Get the line to compare with. */ + p = reg_getline(clnum); + if (clnum == end_lnum) + len = end_col - ccol; + else + len = (int)STRLEN(p + ccol); + + if (cstrncmp(p + ccol, reginput, &len) != 0) + return RA_NOMATCH; /* doesn't match */ + if (bytelen != NULL) + *bytelen += len; + if (clnum == end_lnum) + break; /* match and at end! */ + if (reglnum >= reg_maxline) + return RA_NOMATCH; /* text too short */ + + /* Advance to next line. */ + reg_nextline(); + ++clnum; + ccol = 0; + if (got_int) + return RA_FAIL; + } + + /* found a match! Note that regline may now point to a copy of the line, + * that should not matter. */ + return RA_MATCH; + } #ifdef BT_REGEXP_DUMP *** ../vim-7.3.1190/src/regexp_nfa.c 2013-06-13 22:59:25.000000000 +0200 --- src/regexp_nfa.c 2013-06-14 20:19:59.000000000 +0200 *************** *** 4367,4380 **** if (sub->list.multi[subidx].start.lnum < 0 || sub->list.multi[subidx].end.lnum < 0) goto retempty; ! /* TODO: line breaks */ ! len = sub->list.multi[subidx].end.col ! - sub->list.multi[subidx].start.col; ! if (cstrncmp(regline + sub->list.multi[subidx].start.col, ! reginput, &len) == 0) { ! *bytelen = len; ! return TRUE; } } else --- 4367,4393 ---- if (sub->list.multi[subidx].start.lnum < 0 || sub->list.multi[subidx].end.lnum < 0) goto retempty; ! if (sub->list.multi[subidx].start.lnum == reglnum ! && sub->list.multi[subidx].end.lnum == reglnum) { ! len = sub->list.multi[subidx].end.col ! - sub->list.multi[subidx].start.col; ! if (cstrncmp(regline + sub->list.multi[subidx].start.col, ! reginput, &len) == 0) ! { ! *bytelen = len; ! return TRUE; ! } ! } ! else ! { ! if (match_with_backref( ! sub->list.multi[subidx].start.lnum, ! sub->list.multi[subidx].start.col, ! sub->list.multi[subidx].end.lnum, ! sub->list.multi[subidx].end.col, ! bytelen) == RA_MATCH) ! return TRUE; } } else *** ../vim-7.3.1190/src/testdir/test64.in 2013-06-13 20:19:35.000000000 +0200 --- src/testdir/test64.in 2013-06-14 20:01:56.000000000 +0200 *************** *** 486,491 **** --- 486,497 ---- :.yank Gop:" :" + :" Check using a backref matching in a previous line + /^Backref: + /\v.*\/(.*)\n.*\/\1$ + :.yank + Gop:" + :" :" Check a pattern with a look beind crossing a line boundary /^Behind: /\(<\_[xy]\+\)\@3<=start *************** *** 566,571 **** --- 572,584 ---- b c + Backref: + ./Dir1/Dir2/zyxwvuts.txt + ./Dir1/Dir2/abcdefgh.bat + + ./Dir1/Dir2/file1.txt + ./OtherDir1/OtherDir2/file1.txt + Behind: asdfasd