1  | 
     | 
     | 
    /*	$OpenBSD: ex_global.c,v 1.17 2016/05/27 09:18:12 martijn Exp $	*/  | 
    
    
    2  | 
     | 
     | 
     | 
    
    
    3  | 
     | 
     | 
    /*-  | 
    
    
    4  | 
     | 
     | 
     * Copyright (c) 1992, 1993, 1994  | 
    
    
    5  | 
     | 
     | 
     *	The Regents of the University of California.  All rights reserved.  | 
    
    
    6  | 
     | 
     | 
     * Copyright (c) 1992, 1993, 1994, 1995, 1996  | 
    
    
    7  | 
     | 
     | 
     *	Keith Bostic.  All rights reserved.  | 
    
    
    8  | 
     | 
     | 
     *  | 
    
    
    9  | 
     | 
     | 
     * See the LICENSE file for redistribution information.  | 
    
    
    10  | 
     | 
     | 
     */  | 
    
    
    11  | 
     | 
     | 
     | 
    
    
    12  | 
     | 
     | 
    #include "config.h"  | 
    
    
    13  | 
     | 
     | 
     | 
    
    
    14  | 
     | 
     | 
    #include <sys/types.h>  | 
    
    
    15  | 
     | 
     | 
    #include <sys/queue.h>  | 
    
    
    16  | 
     | 
     | 
     | 
    
    
    17  | 
     | 
     | 
    #include <bitstring.h>  | 
    
    
    18  | 
     | 
     | 
    #include <ctype.h>  | 
    
    
    19  | 
     | 
     | 
    #include <errno.h>  | 
    
    
    20  | 
     | 
     | 
    #include <limits.h>  | 
    
    
    21  | 
     | 
     | 
    #include <stdio.h>  | 
    
    
    22  | 
     | 
     | 
    #include <stdlib.h>  | 
    
    
    23  | 
     | 
     | 
    #include <string.h>  | 
    
    
    24  | 
     | 
     | 
    #include <unistd.h>  | 
    
    
    25  | 
     | 
     | 
     | 
    
    
    26  | 
     | 
     | 
    #include "../common/common.h"  | 
    
    
    27  | 
     | 
     | 
     | 
    
    
    28  | 
     | 
     | 
    enum which {GLOBAL, V}; | 
    
    
    29  | 
     | 
     | 
     | 
    
    
    30  | 
     | 
     | 
    static int ex_g_setup(SCR *, EXCMD *, enum which);  | 
    
    
    31  | 
     | 
     | 
     | 
    
    
    32  | 
     | 
     | 
    /*  | 
    
    
    33  | 
     | 
     | 
     * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]  | 
    
    
    34  | 
     | 
     | 
     *	Exec on lines matching a pattern.  | 
    
    
    35  | 
     | 
     | 
     *  | 
    
    
    36  | 
     | 
     | 
     * PUBLIC: int ex_global(SCR *, EXCMD *);  | 
    
    
    37  | 
     | 
     | 
     */  | 
    
    
    38  | 
     | 
     | 
    int  | 
    
    
    39  | 
     | 
     | 
    ex_global(SCR *sp, EXCMD *cmdp)  | 
    
    
    40  | 
     | 
     | 
    { | 
    
    
    41  | 
     | 
     | 
    	return (ex_g_setup(sp,  | 
    
    
    42  | 
     | 
     | 
    	    cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL));  | 
    
    
    43  | 
     | 
     | 
    }  | 
    
    
    44  | 
     | 
     | 
     | 
    
    
    45  | 
     | 
     | 
    /*  | 
    
    
    46  | 
     | 
     | 
     * ex_v -- [line [,line]] v /pattern/ [commands]  | 
    
    
    47  | 
     | 
     | 
     *	Exec on lines not matching a pattern.  | 
    
    
    48  | 
     | 
     | 
     *  | 
    
    
    49  | 
     | 
     | 
     * PUBLIC: int ex_v(SCR *, EXCMD *);  | 
    
    
    50  | 
     | 
     | 
     */  | 
    
    
    51  | 
     | 
     | 
    int  | 
    
    
    52  | 
     | 
     | 
    ex_v(SCR *sp, EXCMD *cmdp)  | 
    
    
    53  | 
     | 
     | 
    { | 
    
    
    54  | 
     | 
     | 
    	return (ex_g_setup(sp, cmdp, V));  | 
    
    
    55  | 
     | 
     | 
    }  | 
    
    
    56  | 
     | 
     | 
     | 
    
    
    57  | 
     | 
     | 
    /*  | 
    
    
    58  | 
     | 
     | 
     * ex_g_setup --  | 
    
    
    59  | 
     | 
     | 
     *	Ex global and v commands.  | 
    
    
    60  | 
     | 
     | 
     */  | 
    
    
    61  | 
     | 
     | 
    static int  | 
    
    
    62  | 
     | 
     | 
    ex_g_setup(SCR *sp, EXCMD *cmdp, enum which cmd)  | 
    
    
    63  | 
     | 
     | 
    { | 
    
    
    64  | 
     | 
     | 
    	CHAR_T *ptrn, *p, *t;  | 
    
    
    65  | 
     | 
     | 
    	EXCMD *ecp;  | 
    
    
    66  | 
     | 
     | 
    	MARK abs_mark;  | 
    
    
    67  | 
     | 
     | 
    	RANGE *rp;  | 
    
    
    68  | 
     | 
     | 
    	busy_t btype;  | 
    
    
    69  | 
     | 
     | 
    	recno_t start, end;  | 
    
    
    70  | 
     | 
     | 
    	regex_t *re;  | 
    
    
    71  | 
     | 
     | 
    	regmatch_t match[1];  | 
    
    
    72  | 
     | 
     | 
    	size_t len;  | 
    
    
    73  | 
     | 
     | 
    	int cnt, delim, eval;  | 
    
    
    74  | 
     | 
     | 
    	char *dbp;  | 
    
    
    75  | 
     | 
     | 
     | 
    
    
    76  | 
     | 
     | 
    	NEEDFILE(sp, cmdp);  | 
    
    
    77  | 
     | 
     | 
     | 
    
    
    78  | 
     | 
     | 
    	if (F_ISSET(sp, SC_EX_GLOBAL)) { | 
    
    
    79  | 
     | 
     | 
    		msgq(sp, M_ERR,  | 
    
    
    80  | 
     | 
     | 
    	"The %s command can't be used as part of a global or v command",  | 
    
    
    81  | 
     | 
     | 
    		    cmdp->cmd->name);  | 
    
    
    82  | 
     | 
     | 
    		return (1);  | 
    
    
    83  | 
     | 
     | 
    	}  | 
    
    
    84  | 
     | 
     | 
     | 
    
    
    85  | 
     | 
     | 
    	/*  | 
    
    
    86  | 
     | 
     | 
    	 * Skip leading white space.  Historic vi allowed any non-alphanumeric  | 
    
    
    87  | 
     | 
     | 
    	 * to serve as the global command delimiter.  | 
    
    
    88  | 
     | 
     | 
    	 */  | 
    
    
    89  | 
     | 
     | 
    	if (cmdp->argc == 0)  | 
    
    
    90  | 
     | 
     | 
    		goto usage;  | 
    
    
    91  | 
     | 
     | 
    	for (p = cmdp->argv[0]->bp; isblank(*p); ++p);  | 
    
    
    92  | 
     | 
     | 
    	if (*p == '\0' || isalnum(*p) ||  | 
    
    
    93  | 
     | 
     | 
    	    *p == '\\' || *p == '|' || *p == '\n') { | 
    
    
    94  | 
     | 
     | 
    usage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);  | 
    
    
    95  | 
     | 
     | 
    		return (1);  | 
    
    
    96  | 
     | 
     | 
    	}  | 
    
    
    97  | 
     | 
     | 
    	delim = *p++;  | 
    
    
    98  | 
     | 
     | 
     | 
    
    
    99  | 
     | 
     | 
    	/*  | 
    
    
    100  | 
     | 
     | 
    	 * Get the pattern string, toss escaped characters.  | 
    
    
    101  | 
     | 
     | 
    	 *  | 
    
    
    102  | 
     | 
     | 
    	 * QUOTING NOTE:  | 
    
    
    103  | 
     | 
     | 
    	 * Only toss an escaped character if it escapes a delimiter.  | 
    
    
    104  | 
     | 
     | 
    	 */  | 
    
    
    105  | 
     | 
     | 
    	for (ptrn = t = p;;) { | 
    
    
    106  | 
     | 
     | 
    		if (p[0] == '\0' || p[0] == delim) { | 
    
    
    107  | 
     | 
     | 
    			if (p[0] == delim)  | 
    
    
    108  | 
     | 
     | 
    				++p;  | 
    
    
    109  | 
     | 
     | 
    			/*  | 
    
    
    110  | 
     | 
     | 
    			 * !!!  | 
    
    
    111  | 
     | 
     | 
    			 * Nul terminate the pattern string -- it's passed  | 
    
    
    112  | 
     | 
     | 
    			 * to regcomp which doesn't understand anything else.  | 
    
    
    113  | 
     | 
     | 
    			 */  | 
    
    
    114  | 
     | 
     | 
    			*t = '\0';  | 
    
    
    115  | 
     | 
     | 
    			break;  | 
    
    
    116  | 
     | 
     | 
    		}  | 
    
    
    117  | 
     | 
     | 
    		if (p[0] == '\\') { | 
    
    
    118  | 
     | 
     | 
    			if (p[1] == delim)  | 
    
    
    119  | 
     | 
     | 
    				++p;  | 
    
    
    120  | 
     | 
     | 
    			else if (p[1] == '\\')  | 
    
    
    121  | 
     | 
     | 
    				*t++ = *p++;  | 
    
    
    122  | 
     | 
     | 
    		}  | 
    
    
    123  | 
     | 
     | 
    		*t++ = *p++;  | 
    
    
    124  | 
     | 
     | 
    	}  | 
    
    
    125  | 
     | 
     | 
     | 
    
    
    126  | 
     | 
     | 
    	/* If the pattern string is empty, use the last one. */  | 
    
    
    127  | 
     | 
     | 
    	if (*ptrn == '\0') { | 
    
    
    128  | 
     | 
     | 
    		if (sp->re == NULL) { | 
    
    
    129  | 
     | 
     | 
    			ex_emsg(sp, NULL, EXM_NOPREVRE);  | 
    
    
    130  | 
     | 
     | 
    			return (1);  | 
    
    
    131  | 
     | 
     | 
    		}  | 
    
    
    132  | 
     | 
     | 
     | 
    
    
    133  | 
     | 
     | 
    		/* Re-compile the RE if necessary. */  | 
    
    
    134  | 
     | 
     | 
    		if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,  | 
    
    
    135  | 
     | 
     | 
    		    sp->re, sp->re_len, NULL, NULL, &sp->re_c, RE_C_SEARCH))  | 
    
    
    136  | 
     | 
     | 
    			return (1);  | 
    
    
    137  | 
     | 
     | 
    	} else { | 
    
    
    138  | 
     | 
     | 
    		/* Compile the RE. */  | 
    
    
    139  | 
     | 
     | 
    		if (re_compile(sp, ptrn, t - ptrn,  | 
    
    
    140  | 
     | 
     | 
    		    &sp->re, &sp->re_len, &sp->re_c, RE_C_SEARCH))  | 
    
    
    141  | 
     | 
     | 
    			return (1);  | 
    
    
    142  | 
     | 
     | 
     | 
    
    
    143  | 
     | 
     | 
    		/*  | 
    
    
    144  | 
     | 
     | 
    		 * Set saved RE.  Historic practice is that globals set  | 
    
    
    145  | 
     | 
     | 
    		 * direction as well as the RE.  | 
    
    
    146  | 
     | 
     | 
    		 */  | 
    
    
    147  | 
     | 
     | 
    		sp->searchdir = FORWARD;  | 
    
    
    148  | 
     | 
     | 
    	}  | 
    
    
    149  | 
     | 
     | 
    	re = &sp->re_c;  | 
    
    
    150  | 
     | 
     | 
     | 
    
    
    151  | 
     | 
     | 
    	/* The global commands always set the previous context mark. */  | 
    
    
    152  | 
     | 
     | 
    	abs_mark.lno = sp->lno;  | 
    
    
    153  | 
     | 
     | 
    	abs_mark.cno = sp->cno;  | 
    
    
    154  | 
     | 
     | 
    	if (mark_set(sp, ABSMARK1, &abs_mark, 1))  | 
    
    
    155  | 
     | 
     | 
    		return (1);  | 
    
    
    156  | 
     | 
     | 
     | 
    
    
    157  | 
     | 
     | 
    	/* Get an EXCMD structure. */  | 
    
    
    158  | 
     | 
     | 
    	CALLOC_RET(sp, ecp, 1, sizeof(EXCMD));  | 
    
    
    159  | 
     | 
     | 
    	TAILQ_INIT(&ecp->rq);  | 
    
    
    160  | 
     | 
     | 
     | 
    
    
    161  | 
     | 
     | 
    	/*  | 
    
    
    162  | 
     | 
     | 
    	 * Get a copy of the command string; the default command is print.  | 
    
    
    163  | 
     | 
     | 
    	 * Don't worry about a set of <blank>s with no command, that will  | 
    
    
    164  | 
     | 
     | 
    	 * default to print in the ex parser.  We need to have two copies  | 
    
    
    165  | 
     | 
     | 
    	 * because the ex parser may step on the command string when it's  | 
    
    
    166  | 
     | 
     | 
    	 * parsing it.  | 
    
    
    167  | 
     | 
     | 
    	 */  | 
    
    
    168  | 
     | 
     | 
    	if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) { | 
    
    
    169  | 
     | 
     | 
    		p = "pp";  | 
    
    
    170  | 
     | 
     | 
    		len = 1;  | 
    
    
    171  | 
     | 
     | 
    	}  | 
    
    
    172  | 
     | 
     | 
     | 
    
    
    173  | 
     | 
     | 
    	MALLOC_RET(sp, ecp->cp, len * 2);  | 
    
    
    174  | 
     | 
     | 
    	ecp->o_cp = ecp->cp;  | 
    
    
    175  | 
     | 
     | 
    	ecp->o_clen = len;  | 
    
    
    176  | 
     | 
     | 
    	memcpy(ecp->cp + len, p, len);  | 
    
    
    177  | 
     | 
     | 
    	ecp->range_lno = OOBLNO;  | 
    
    
    178  | 
     | 
     | 
    	FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V);  | 
    
    
    179  | 
     | 
     | 
    	LIST_INSERT_HEAD(&sp->gp->ecq, ecp, q);  | 
    
    
    180  | 
     | 
     | 
     | 
    
    
    181  | 
     | 
     | 
    	/*  | 
    
    
    182  | 
     | 
     | 
    	 * For each line...  The semantics of global matching are that we first  | 
    
    
    183  | 
     | 
     | 
    	 * have to decide which lines are going to get passed to the command,  | 
    
    
    184  | 
     | 
     | 
    	 * and then pass them to the command, ignoring other changes.  There's  | 
    
    
    185  | 
     | 
     | 
    	 * really no way to do this in a single pass, since arbitrary line  | 
    
    
    186  | 
     | 
     | 
    	 * creation, deletion and movement can be done in the ex command.  For  | 
    
    
    187  | 
     | 
     | 
    	 * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".  | 
    
    
    188  | 
     | 
     | 
    	 * What we do is create linked list of lines that are tracked through  | 
    
    
    189  | 
     | 
     | 
    	 * each ex command.  There's a callback routine which the DB interface  | 
    
    
    190  | 
     | 
     | 
    	 * routines call when a line is created or deleted.  This doesn't help  | 
    
    
    191  | 
     | 
     | 
    	 * the layering much.  | 
    
    
    192  | 
     | 
     | 
    	 */  | 
    
    
    193  | 
     | 
     | 
    	btype = BUSY_ON;  | 
    
    
    194  | 
     | 
     | 
    	cnt = INTERRUPT_CHECK;  | 
    
    
    195  | 
     | 
     | 
    	for (start = cmdp->addr1.lno,  | 
    
    
    196  | 
     | 
     | 
    	    end = cmdp->addr2.lno; start <= end; ++start) { | 
    
    
    197  | 
     | 
     | 
    		if (cnt-- == 0) { | 
    
    
    198  | 
     | 
     | 
    			if (INTERRUPTED(sp)) { | 
    
    
    199  | 
     | 
     | 
    				LIST_REMOVE(ecp, q);  | 
    
    
    200  | 
     | 
     | 
    				free(ecp->cp);  | 
    
    
    201  | 
     | 
     | 
    				free(ecp);  | 
    
    
    202  | 
     | 
     | 
    				break;  | 
    
    
    203  | 
     | 
     | 
    			}  | 
    
    
    204  | 
     | 
     | 
    			search_busy(sp, btype);  | 
    
    
    205  | 
     | 
     | 
    			btype = BUSY_UPDATE;  | 
    
    
    206  | 
     | 
     | 
    			cnt = INTERRUPT_CHECK;  | 
    
    
    207  | 
     | 
     | 
    		}  | 
    
    
    208  | 
     | 
     | 
    		if (db_get(sp, start, DBG_FATAL, &dbp, &len))  | 
    
    
    209  | 
     | 
     | 
    			return (1);  | 
    
    
    210  | 
     | 
     | 
    		match[0].rm_so = 0;  | 
    
    
    211  | 
     | 
     | 
    		match[0].rm_eo = len;  | 
    
    
    212  | 
     | 
     | 
    		switch (eval =  | 
    
    
    213  | 
     | 
     | 
    		    regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) { | 
    
    
    214  | 
     | 
     | 
    		case 0:  | 
    
    
    215  | 
     | 
     | 
    			if (cmd == V)  | 
    
    
    216  | 
     | 
     | 
    				continue;  | 
    
    
    217  | 
     | 
     | 
    			break;  | 
    
    
    218  | 
     | 
     | 
    		case REG_NOMATCH:  | 
    
    
    219  | 
     | 
     | 
    			if (cmd == GLOBAL)  | 
    
    
    220  | 
     | 
     | 
    				continue;  | 
    
    
    221  | 
     | 
     | 
    			break;  | 
    
    
    222  | 
     | 
     | 
    		default:  | 
    
    
    223  | 
     | 
     | 
    			re_error(sp, eval, &sp->re_c);  | 
    
    
    224  | 
     | 
     | 
    			break;  | 
    
    
    225  | 
     | 
     | 
    		}  | 
    
    
    226  | 
     | 
     | 
     | 
    
    
    227  | 
     | 
     | 
    		/* If follows the last entry, extend the last entry's range. */  | 
    
    
    228  | 
     | 
     | 
    		if ((rp = TAILQ_LAST(&ecp->rq, _rh)) && rp->stop == start - 1) { | 
    
    
    229  | 
     | 
     | 
    			++rp->stop;  | 
    
    
    230  | 
     | 
     | 
    			continue;  | 
    
    
    231  | 
     | 
     | 
    		}  | 
    
    
    232  | 
     | 
     | 
     | 
    
    
    233  | 
     | 
     | 
    		/* Allocate a new range, and append it to the list. */  | 
    
    
    234  | 
     | 
     | 
    		CALLOC(sp, rp, 1, sizeof(RANGE));  | 
    
    
    235  | 
     | 
     | 
    		if (rp == NULL)  | 
    
    
    236  | 
     | 
     | 
    			return (1);  | 
    
    
    237  | 
     | 
     | 
    		rp->start = rp->stop = start;  | 
    
    
    238  | 
     | 
     | 
    		TAILQ_INSERT_TAIL(&ecp->rq, rp, q);  | 
    
    
    239  | 
     | 
     | 
    	}  | 
    
    
    240  | 
     | 
     | 
    	search_busy(sp, BUSY_OFF);  | 
    
    
    241  | 
     | 
     | 
    	return (0);  | 
    
    
    242  | 
     | 
     | 
    }  | 
    
    
    243  | 
     | 
     | 
     | 
    
    
    244  | 
     | 
     | 
    /*  | 
    
    
    245  | 
     | 
     | 
     * ex_g_insdel --  | 
    
    
    246  | 
     | 
     | 
     *	Update the ranges based on an insertion or deletion.  | 
    
    
    247  | 
     | 
     | 
     *  | 
    
    
    248  | 
     | 
     | 
     * PUBLIC: int ex_g_insdel(SCR *, lnop_t, recno_t);  | 
    
    
    249  | 
     | 
     | 
     */  | 
    
    
    250  | 
     | 
     | 
    int  | 
    
    
    251  | 
     | 
     | 
    ex_g_insdel(SCR *sp, lnop_t op, recno_t lno)  | 
    
    
    252  | 
     | 
     | 
    { | 
    
    
    253  | 
     | 
     | 
    	EXCMD *ecp;  | 
    
    
    254  | 
     | 
     | 
    	RANGE *nrp, *rp;  | 
    
    
    255  | 
     | 
     | 
     | 
    
    
    256  | 
     | 
     | 
    	/* All insert/append operations are done as inserts. */  | 
    
    
    257  | 
     | 
     | 
    	if (op == LINE_APPEND)  | 
    
    
    258  | 
     | 
     | 
    		abort();  | 
    
    
    259  | 
     | 
     | 
     | 
    
    
    260  | 
     | 
     | 
    	if (op == LINE_RESET)  | 
    
    
    261  | 
     | 
     | 
    		return (0);  | 
    
    
    262  | 
     | 
     | 
     | 
    
    
    263  | 
     | 
     | 
    	LIST_FOREACH(ecp, &sp->gp->ecq, q) { | 
    
    
    264  | 
     | 
     | 
    		if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V))  | 
    
    
    265  | 
     | 
     | 
    			continue;  | 
    
    
    266  | 
     | 
     | 
    		for (rp = TAILQ_FIRST(&ecp->rq); rp != NULL; rp = nrp) { | 
    
    
    267  | 
     | 
     | 
    			nrp = TAILQ_NEXT(rp, q);  | 
    
    
    268  | 
     | 
     | 
     | 
    
    
    269  | 
     | 
     | 
    			/* If range less than the line, ignore it. */  | 
    
    
    270  | 
     | 
     | 
    			if (rp->stop < lno)  | 
    
    
    271  | 
     | 
     | 
    				continue;  | 
    
    
    272  | 
     | 
     | 
     | 
    
    
    273  | 
     | 
     | 
    			/*  | 
    
    
    274  | 
     | 
     | 
    			 * If range greater than the line, decrement or  | 
    
    
    275  | 
     | 
     | 
    			 * increment the range.  | 
    
    
    276  | 
     | 
     | 
    			 */  | 
    
    
    277  | 
     | 
     | 
    			if (rp->start > lno) { | 
    
    
    278  | 
     | 
     | 
    				if (op == LINE_DELETE) { | 
    
    
    279  | 
     | 
     | 
    					--rp->start;  | 
    
    
    280  | 
     | 
     | 
    					--rp->stop;  | 
    
    
    281  | 
     | 
     | 
    				} else { | 
    
    
    282  | 
     | 
     | 
    					++rp->start;  | 
    
    
    283  | 
     | 
     | 
    					++rp->stop;  | 
    
    
    284  | 
     | 
     | 
    				}  | 
    
    
    285  | 
     | 
     | 
    				continue;  | 
    
    
    286  | 
     | 
     | 
    			}  | 
    
    
    287  | 
     | 
     | 
     | 
    
    
    288  | 
     | 
     | 
    			/*  | 
    
    
    289  | 
     | 
     | 
    			 * Lno is inside the range, decrement the end point  | 
    
    
    290  | 
     | 
     | 
    			 * for deletion, and split the range for insertion.  | 
    
    
    291  | 
     | 
     | 
    			 * In the latter case, since we're inserting a new  | 
    
    
    292  | 
     | 
     | 
    			 * element, neither range can be exhausted.  | 
    
    
    293  | 
     | 
     | 
    			 */  | 
    
    
    294  | 
     | 
     | 
    			if (op == LINE_DELETE) { | 
    
    
    295  | 
     | 
     | 
    				if (rp->start > --rp->stop) { | 
    
    
    296  | 
     | 
     | 
    					TAILQ_REMOVE(&ecp->rq, rp, q);  | 
    
    
    297  | 
     | 
     | 
    					free(rp);  | 
    
    
    298  | 
     | 
     | 
    				}  | 
    
    
    299  | 
     | 
     | 
    			} else { | 
    
    
    300  | 
     | 
     | 
    				CALLOC_RET(sp, nrp, 1, sizeof(RANGE));  | 
    
    
    301  | 
     | 
     | 
    				nrp->start = lno + 1;  | 
    
    
    302  | 
     | 
     | 
    				nrp->stop = rp->stop + 1;  | 
    
    
    303  | 
     | 
     | 
    				rp->stop = lno - 1;  | 
    
    
    304  | 
     | 
     | 
    				TAILQ_INSERT_AFTER(&ecp->rq, rp, nrp, q);  | 
    
    
    305  | 
     | 
     | 
    				rp = nrp;  | 
    
    
    306  | 
     | 
     | 
    			}  | 
    
    
    307  | 
     | 
     | 
    		}  | 
    
    
    308  | 
     | 
     | 
     | 
    
    
    309  | 
     | 
     | 
    		/*  | 
    
    
    310  | 
     | 
     | 
    		 * If the command deleted/inserted lines, the cursor moves to  | 
    
    
    311  | 
     | 
     | 
    		 * the line after the deleted/inserted line.  | 
    
    
    312  | 
     | 
     | 
    		 */  | 
    
    
    313  | 
     | 
     | 
    		ecp->range_lno = lno;  | 
    
    
    314  | 
     | 
     | 
    	}  | 
    
    
    315  | 
     | 
     | 
    	return (0);  | 
    
    
    316  | 
     | 
     | 
    }  |