From: Walter Alejandro Iglesias Subject: vi(1) count paste bug. New diff. To: tech@openbsd.org Date: Sun, 10 Aug 2025 12:05:21 +0200 New approach diff for this bug: https://marc.info/?l=openbsd-bugs&m=175456507407063&w=2 Perhaps some people won't like the size of this new version of my patch, especially considering it does the same thing as the previous one (this is NOT the second alternative I promised in bugs@ message), it takes up space because I had to add an argument to the put() function and modify this function in several files. What motivated me to look for a second solution is that the first one seemed a bit hackish to me (the whole vi/v_put.c is kind of a hack, to tell the truth), so I looked for a more robust approach which also would allowed me, in the future, to deal with a second issue (this is more an inconvenience than a bug) related to "where the cursor lands" behavior. This version implements the count prefix in the function put() itself (common/put.c), this means that the whole string with repetitions is loaded into the same buffer before it is pasted. With this approach, to modify the "where the cursor lands" behavior (in case there's consensus in doing this) will take modifying just one line. Index: common/put.c =================================================================== RCS file: /cvs/src/usr.bin/vi/common/put.c,v diff -u -p -r1.16 put.c --- common/put.c 27 May 2016 09:18:11 -0000 1.16 +++ common/put.c 9 Aug 2025 14:53:56 -0000 @@ -27,16 +27,16 @@ * put -- * Put text buffer contents into the file. * - * PUBLIC: int put(SCR *, CB *, CHAR_T *, MARK *, MARK *, int); + * PUBLIC: int put(SCR *, CB *, CHAR_T *, MARK *, MARK *, int, int); */ int -put(SCR *sp, CB *cbp, CHAR_T *namep, MARK *cp, MARK *rp, int append) +put(SCR *sp, CB *cbp, CHAR_T *namep, MARK *cp, MARK *rp, int append, int cnt) { CHAR_T name; TEXT *ltp, *tp; recno_t lno; size_t blen, clen, len; - int rval; + int rval, i; char *bp, *p, *t; if (cbp == NULL) { @@ -78,10 +78,14 @@ put(SCR *sp, CB *cbp, CHAR_T *namep, MAR if (db_last(sp, &lno)) return (1); if (lno == 0) { - for (; tp; ++lno, ++sp->rptlines[L_ADDED], - tp = TAILQ_NEXT(tp, q)) - if (db_append(sp, 1, lno, tp->lb, tp->len)) - return (1); + for (i = cnt; i > 0; i--) { + for (; tp; ++lno, ++sp->rptlines[L_ADDED], + tp = TAILQ_NEXT(tp, q)) + if (db_append(sp, 1, lno, tp->lb, + tp->len)) + return (1); + tp = TAILQ_FIRST(&cbp->textq); + } rp->lno = 1; rp->cno = 0; return (0); @@ -92,10 +96,14 @@ put(SCR *sp, CB *cbp, CHAR_T *namep, MAR if (F_ISSET(cbp, CB_LMODE)) { lno = append ? cp->lno : cp->lno - 1; rp->lno = lno + 1; - for (; tp; - ++lno, ++sp->rptlines[L_ADDED], tp = TAILQ_NEXT(tp, q)) - if (db_append(sp, 1, lno, tp->lb, tp->len)) - return (1); + for (i = cnt; i > 0; i--) { + for (; tp; + ++lno, ++sp->rptlines[L_ADDED], + tp = TAILQ_NEXT(tp, q)) + if (db_append(sp, 1, lno, tp->lb, tp->len)) + return (1); + tp = TAILQ_FIRST(&cbp->textq); + } rp->cno = 0; (void)nonblank(sp, rp->lno, &rp->cno); return (0); @@ -126,8 +134,10 @@ put(SCR *sp, CB *cbp, CHAR_T *namep, MAR /* First line from the CB. */ if (tp->len != 0) { - memcpy(t, tp->lb, tp->len); - t += tp->len; + for (i = cnt; i > 0; i--) { + memcpy(t, tp->lb, tp->len); + t += tp->len; + } } /* Calculate length left in the original line. */ Index: ex/ex_move.c =================================================================== RCS file: /cvs/src/usr.bin/vi/ex/ex_move.c,v diff -u -p -r1.11 ex_move.c --- ex/ex_move.c 6 Jan 2016 22:28:52 -0000 1.11 +++ ex/ex_move.c 9 Aug 2025 14:53:56 -0000 @@ -59,7 +59,7 @@ ex_copy(SCR *sp, EXCMD *cmdp) /* Put the text into place. */ tm.lno = cmdp->lineno; tm.cno = 0; - if (put(sp, &cb, NULL, &tm, &m, 1)) + if (put(sp, &cb, NULL, &tm, &m, 1,1)) rval = 1; else { /* Index: ex/ex_put.c =================================================================== RCS file: /cvs/src/usr.bin/vi/ex/ex_put.c,v diff -u -p -r1.6 ex_put.c --- ex/ex_put.c 12 Nov 2014 04:28:41 -0000 1.6 +++ ex/ex_put.c 9 Aug 2025 14:53:56 -0000 @@ -39,7 +39,7 @@ ex_put(SCR *sp, EXCMD *cmdp) m.cno = sp->cno; if (put(sp, NULL, FL_ISSET(cmdp->iflags, E_C_BUFFER) ? &cmdp->buffer : NULL, - &cmdp->addr1, &m, 1)) + &cmdp->addr1, &m, 1, 1)) return (1); sp->lno = m.lno; sp->cno = m.cno; Index: include/com_extern.h =================================================================== RCS file: /cvs/src/usr.bin/vi/include/com_extern.h,v diff -u -p -r1.16 com_extern.h --- include/com_extern.h 21 May 2019 09:24:58 -0000 1.16 +++ include/com_extern.h 9 Aug 2025 14:53:56 -0000 @@ -81,7 +81,7 @@ int f_w300(SCR *, OPTION *, char *, u_lo int f_w1200(SCR *, OPTION *, char *, u_long *); int f_w9600(SCR *, OPTION *, char *, u_long *); int f_window(SCR *, OPTION *, char *, u_long *); -int put(SCR *, CB *, CHAR_T *, MARK *, MARK *, int); +int put(SCR *, CB *, CHAR_T *, MARK *, MARK *, int, int); int rcv_tmp(SCR *, EXF *, char *); int rcv_init(SCR *); int rcv_sync(SCR *, u_int); Index: vi/v_put.c =================================================================== RCS file: /cvs/src/usr.bin/vi/vi/v_put.c,v diff -u -p -r1.8 v_put.c --- vi/v_put.c 27 May 2016 09:18:12 -0000 1.8 +++ vi/v_put.c 9 Aug 2025 14:53:56 -0000 @@ -43,15 +43,14 @@ v_Put(SCR *sp, VICMD *vp) * Historic vi did not support a count with the 'p' and 'P' * commands. It's useful, so we do. */ - for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { - if (put(sp, NULL, - F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, - &vp->m_start, &vp->m_final, 0)) - return (1); - vp->m_start = vp->m_final; - if (INTERRUPTED(sp)) - return (1); - } + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + if (put(sp, NULL, + F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + &vp->m_start, &vp->m_final, 0, cnt)) + return (1); + vp->m_start = vp->m_final; + if (INTERRUPTED(sp)) + return (1); return (0); } @@ -74,15 +73,14 @@ v_put(SCR *sp, VICMD *vp) * Historic vi did not support a count with the 'p' and 'P' * commands. It's useful, so we do. */ - for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) { - if (put(sp, NULL, - F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, - &vp->m_start, &vp->m_final, 1)) - return (1); - vp->m_start = vp->m_final; - if (INTERRUPTED(sp)) - return (1); - } + cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; + if (put(sp, NULL, + F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, + &vp->m_start, &vp->m_final, 1, cnt)) + return (1); + vp->m_start = vp->m_final; + if (INTERRUPTED(sp)) + return (1); return (0); } -- Walter