Home | History | Annotate | Line # | Download | only in vi
      1 /*	$NetBSD: v_ex.c,v 1.7 2014/01/26 21:43:45 christos Exp $ */
      2 /*-
      3  * Copyright (c) 1992, 1993, 1994
      4  *	The Regents of the University of California.  All rights reserved.
      5  * Copyright (c) 1992, 1993, 1994, 1995, 1996
      6  *	Keith Bostic.  All rights reserved.
      7  *
      8  * See the LICENSE file for redistribution information.
      9  */
     10 
     11 #include "config.h"
     12 
     13 #include <sys/cdefs.h>
     14 #if 0
     15 #ifndef lint
     16 static const char sccsid[] = "Id: v_ex.c,v 10.60 2003/07/19 21:04:00 skimo Exp  (Berkeley) Date: 2003/07/19 21:04:00 ";
     17 #endif /* not lint */
     18 #else
     19 __RCSID("$NetBSD: v_ex.c,v 1.7 2014/01/26 21:43:45 christos Exp $");
     20 #endif
     21 
     22 #include <sys/types.h>
     23 #include <sys/queue.h>
     24 #include <sys/time.h>
     25 
     26 #include <bitstring.h>
     27 #include <limits.h>
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <unistd.h>
     32 
     33 #include "../common/common.h"
     34 #include "vi.h"
     35 
     36 static int v_ecl __P((SCR *));
     37 static int v_ecl_init __P((SCR *));
     38 static int v_ecl_log __P((SCR *, TEXT *));
     39 static int v_ex_done __P((SCR *, VICMD *));
     40 
     41 /*
     42  * v_again -- &
     43  *	Repeat the previous substitution.
     44  *
     45  * PUBLIC: int v_again __P((SCR *, VICMD *));
     46  */
     47 int
     48 v_again(SCR *sp, VICMD *vp)
     49 {
     50 	EXCMD cmd;
     51 	static CHAR_T nul[] = { 0 };
     52 
     53 	ex_cinit(sp, &cmd, C_SUBAGAIN, 2, vp->m_start.lno, vp->m_start.lno, 1);
     54 	argv_exp0(sp, &cmd, nul, 1);
     55 	return (v_exec_ex(sp, vp, &cmd));
     56 }
     57 
     58 /*
     59  * v_exmode -- Q
     60  *	Switch the editor into EX mode.
     61  *
     62  * PUBLIC: int v_exmode __P((SCR *, VICMD *));
     63  */
     64 int
     65 v_exmode(SCR *sp, VICMD *vp)
     66 {
     67 	GS *gp;
     68 
     69 	gp = sp->gp;
     70 
     71 	/* Try and switch screens -- the screen may not permit it. */
     72 	if (gp->scr_screen(sp, SC_EX)) {
     73 		msgq(sp, M_ERR,
     74 		    "207|The Q command requires the ex terminal interface");
     75 		return (1);
     76 	}
     77 	(void)gp->scr_attr(sp, SA_ALTERNATE, 0);
     78 
     79 	/* Save the current cursor position. */
     80 	sp->frp->lno = sp->lno;
     81 	sp->frp->cno = sp->cno;
     82 	F_SET(sp->frp, FR_CURSORSET);
     83 
     84 	/* Switch to ex mode. */
     85 	F_CLR(sp, SC_VI | SC_SCR_VI);
     86 	F_SET(sp, SC_EX);
     87 
     88 	/* Move out of the vi screen. */
     89 	(void)ex_puts(sp, "\n");
     90 
     91 	return (0);
     92 }
     93 
     94 /*
     95  * v_join -- [count]J
     96  *	Join lines together.
     97  *
     98  * PUBLIC: int v_join __P((SCR *, VICMD *));
     99  */
    100 int
    101 v_join(SCR *sp, VICMD *vp)
    102 {
    103 	EXCMD cmd;
    104 	int lno;
    105 
    106 	/*
    107 	 * YASC.
    108 	 * The general rule is that '#J' joins # lines, counting the current
    109 	 * line.  However, 'J' and '1J' are the same as '2J', i.e. join the
    110 	 * current and next lines.  This doesn't map well into the ex command
    111 	 * (which takes two line numbers), so we handle it here.  Note that
    112 	 * we never test for EOF -- historically going past the end of file
    113 	 * worked just fine.
    114 	 */
    115 	lno = vp->m_start.lno + 1;
    116 	if (F_ISSET(vp, VC_C1SET) && vp->count > 2)
    117 		lno = vp->m_start.lno + (vp->count - 1);
    118 
    119 	ex_cinit(sp, &cmd, C_JOIN, 2, vp->m_start.lno, lno, 0);
    120 	return (v_exec_ex(sp, vp, &cmd));
    121 }
    122 
    123 /*
    124  * v_shiftl -- [count]<motion
    125  *	Shift lines left.
    126  *
    127  * PUBLIC: int v_shiftl __P((SCR *, VICMD *));
    128  */
    129 int
    130 v_shiftl(SCR *sp, VICMD *vp)
    131 {
    132 	EXCMD cmd;
    133 	static CHAR_T lt[] = {'<', 0};
    134 
    135 	ex_cinit(sp, &cmd, C_SHIFTL, 2, vp->m_start.lno, vp->m_stop.lno, 0);
    136 	argv_exp0(sp, &cmd, lt, 2);
    137 	return (v_exec_ex(sp, vp, &cmd));
    138 }
    139 
    140 /*
    141  * v_shiftr -- [count]>motion
    142  *	Shift lines right.
    143  *
    144  * PUBLIC: int v_shiftr __P((SCR *, VICMD *));
    145  */
    146 int
    147 v_shiftr(SCR *sp, VICMD *vp)
    148 {
    149 	EXCMD cmd;
    150 	static CHAR_T gt[] = {'>', 0};
    151 
    152 	ex_cinit(sp, &cmd, C_SHIFTR, 2, vp->m_start.lno, vp->m_stop.lno, 0);
    153 	argv_exp0(sp, &cmd, gt, 2);
    154 	return (v_exec_ex(sp, vp, &cmd));
    155 }
    156 
    157 /*
    158  * v_suspend -- ^Z
    159  *	Suspend vi.
    160  *
    161  * PUBLIC: int v_suspend __P((SCR *, VICMD *));
    162  */
    163 int
    164 v_suspend(SCR *sp, VICMD *vp)
    165 {
    166 	EXCMD cmd;
    167 	static CHAR_T suspend[] = {'s', 'u', 's', 'p', 'e', 'n', 'd', 0};
    168 
    169 	ex_cinit(sp, &cmd, C_STOP, 0, OOBLNO, OOBLNO, 0);
    170 	argv_exp0(sp, &cmd, suspend, sizeof(suspend)/sizeof(CHAR_T));
    171 	return (v_exec_ex(sp, vp, &cmd));
    172 }
    173 
    174 /*
    175  * v_switch -- ^^
    176  *	Switch to the previous file.
    177  *
    178  * PUBLIC: int v_switch __P((SCR *, VICMD *));
    179  */
    180 int
    181 v_switch(SCR *sp, VICMD *vp)
    182 {
    183 	EXCMD cmd;
    184 	char *name;
    185 	const CHAR_T *wp;
    186 	size_t wlen;
    187 
    188 	/*
    189 	 * Try the alternate file name, then the previous file
    190 	 * name.  Use the real name, not the user's current name.
    191 	 */
    192 	if ((name = sp->alt_name) == NULL) {
    193 		msgq(sp, M_ERR, "180|No previous file to edit");
    194 		return (1);
    195 	}
    196 
    197 	/* If autowrite is set, write out the file. */
    198 	if (file_m1(sp, 0, FS_ALL))
    199 		return (1);
    200 
    201 	ex_cinit(sp, &cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0);
    202 	CHAR2INT(sp, name, strlen(name) + 1, wp, wlen);
    203 	argv_exp0(sp, &cmd, wp, wlen);
    204 	return (v_exec_ex(sp, vp, &cmd));
    205 }
    206 
    207 /*
    208  * v_tagpush -- ^[
    209  *	Do a tag search on the cursor keyword.
    210  *
    211  * PUBLIC: int v_tagpush __P((SCR *, VICMD *));
    212  */
    213 int
    214 v_tagpush(SCR *sp, VICMD *vp)
    215 {
    216 	EXCMD cmd;
    217 
    218 #ifdef GTAGS
    219 	if (O_ISSET(sp, O_GTAGSMODE) && vp->m_start.cno == 0)
    220 		ex_cinit(sp, &cmd, C_RTAG, 0, OOBLNO, 0, 0);
    221 	else
    222 #endif
    223 	ex_cinit(sp, &cmd, C_TAG, 0, OOBLNO, 0, 0);
    224 	argv_exp0(sp, &cmd, VIP(sp)->keyw, STRLEN(VIP(sp)->keyw) + 1);
    225 	return (v_exec_ex(sp, vp, &cmd));
    226 }
    227 
    228 /*
    229  * v_tagpop -- ^T
    230  *	Pop the tags stack.
    231  *
    232  * PUBLIC: int v_tagpop __P((SCR *, VICMD *));
    233  */
    234 int
    235 v_tagpop(SCR *sp, VICMD *vp)
    236 {
    237 	EXCMD cmd;
    238 
    239 	ex_cinit(sp, &cmd, C_TAGPOP, 0, OOBLNO, 0, 0);
    240 	return (v_exec_ex(sp, vp, &cmd));
    241 }
    242 
    243 /*
    244  * v_filter -- [count]!motion command(s)
    245  *	Run range through shell commands, replacing text.
    246  *
    247  * PUBLIC: int v_filter __P((SCR *, VICMD *));
    248  */
    249 int
    250 v_filter(SCR *sp, VICMD *vp)
    251 {
    252 	EXCMD cmd;
    253 	TEXT *tp;
    254 
    255 	/*
    256 	 * !!!
    257 	 * Historical vi permitted "!!" in an empty file, and it's handled
    258 	 * as a special case in the ex_bang routine.  Don't modify this setup
    259 	 * without understanding that one.  In particular, note that we're
    260 	 * manipulating the ex argument structures behind ex's back.
    261 	 *
    262 	 * !!!
    263 	 * Historical vi did not permit the '!' command to be associated with
    264 	 * a non-line oriented motion command, in general, although it did
    265 	 * with search commands.  So, !f; and !w would fail, but !/;<CR>
    266 	 * would succeed, even if they all moved to the same location in the
    267 	 * current line.  I don't see any reason to disallow '!' using any of
    268 	 * the possible motion commands.
    269 	 *
    270 	 * !!!
    271 	 * Historical vi ran the last bang command if N or n was used as the
    272 	 * search motion.
    273 	 */
    274 	if (F_ISSET(vp, VC_ISDOT) ||
    275 	    ISCMD(vp->rkp, 'N') || ISCMD(vp->rkp, 'n')) {
    276 		static CHAR_T bang[] = {'!', 0};
    277 		ex_cinit(sp,
    278 		    &cmd, C_BANG, 2, vp->m_start.lno, vp->m_stop.lno, 0);
    279 		EXP(sp)->argsoff = 0;			/* XXX */
    280 
    281 		if (argv_exp1(sp, &cmd, bang, 1, 1))
    282 			return (1);
    283 		cmd.argc = EXP(sp)->argsoff;		/* XXX */
    284 		cmd.argv = EXP(sp)->args;		/* XXX */
    285 		return (v_exec_ex(sp, vp, &cmd));
    286 	}
    287 
    288 	/* Get the command from the user. */
    289 	if (v_tcmd(sp, vp,
    290 	    '!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_FILEC | TXT_PROMPT))
    291 		return (1);
    292 
    293 	/*
    294 	 * Check to see if the user changed their mind.
    295 	 *
    296 	 * !!!
    297 	 * Entering <escape> on an empty line was historically an error,
    298 	 * this implementation doesn't bother.
    299 	 */
    300 	tp = TAILQ_FIRST(&sp->tiq);
    301 	if (tp->term != TERM_OK) {
    302 		vp->m_final.lno = sp->lno;
    303 		vp->m_final.cno = sp->cno;
    304 		return (0);
    305 	}
    306 
    307 	/* Home the cursor. */
    308 	vs_home(sp);
    309 
    310 	ex_cinit(sp, &cmd, C_BANG, 2, vp->m_start.lno, vp->m_stop.lno, 0);
    311 	EXP(sp)->argsoff = 0;			/* XXX */
    312 
    313 	if (argv_exp1(sp, &cmd, tp->lb + 1, tp->len - 1, 1))
    314 		return (1);
    315 	cmd.argc = EXP(sp)->argsoff;		/* XXX */
    316 	cmd.argv = EXP(sp)->args;		/* XXX */
    317 	return (v_exec_ex(sp, vp, &cmd));
    318 }
    319 
    320 /*
    321  * v_exec_ex --
    322  *	Execute an ex command.
    323  *
    324  * PUBLIC: int v_exec_ex __P((SCR *, VICMD *, EXCMD *));
    325  */
    326 int
    327 v_exec_ex(SCR *sp, VICMD *vp, EXCMD *exp)
    328 {
    329 	int rval;
    330 
    331 	rval = exp->cmd->fn(sp, exp);
    332 	return (v_ex_done(sp, vp) || rval);
    333 }
    334 
    335 /*
    336  * v_ex -- :
    337  *	Execute a colon command line.
    338  *
    339  * PUBLIC: int v_ex __P((SCR *, VICMD *));
    340  */
    341 int
    342 v_ex(SCR *sp, VICMD *vp)
    343 {
    344 	WIN *wp;
    345 	TEXT *tp;
    346 	int do_cedit, do_resolution, ifcontinue;
    347 
    348 	wp = sp->wp;
    349 
    350 	/*
    351 	 * !!!
    352 	 * If we put out more than a single line of messages, or ex trashes
    353 	 * the screen, the user may continue entering ex commands.  We find
    354 	 * this out when we do the screen/message resolution.  We can't enter
    355 	 * completely into ex mode however, because the user can elect to
    356 	 * return into vi mode by entering any key, i.e. we have to be in raw
    357 	 * mode.
    358 	 */
    359 	for (do_cedit = do_resolution = 0;;) {
    360 		/*
    361 		 * !!!
    362 		 * There may already be an ex command waiting to run.  If
    363 		 * so, we continue with it.
    364 		 */
    365 		if (!EXCMD_RUNNING(wp)) {
    366 			/* Get a command. */
    367 			if (v_tcmd(sp, vp, ':',
    368 			    TXT_BS | TXT_CEDIT | TXT_FILEC | TXT_PROMPT))
    369 				return (1);
    370 			tp = TAILQ_FIRST(&sp->tiq);
    371 
    372 			/*
    373 			 * If the user entered a single <esc>, they want to
    374 			 * edit their colon command history.  If they already
    375 			 * entered some text, move it into the edit history.
    376 			 */
    377 			if (tp->term == TERM_CEDIT) {
    378 				if (tp->len > 1 && v_ecl_log(sp, tp))
    379 					return (1);
    380 				do_cedit = 1;
    381 				break;
    382 			}
    383 
    384 			/* If the user didn't enter anything, return. */
    385 			if (tp->term == TERM_BS)
    386 				break;
    387 
    388 			/* Log the command. */
    389 			if (O_STR(sp, O_CEDIT) != NULL)
    390 				(void)v_ecl_log(sp, tp);
    391 
    392 			/* Push a command on the command stack. */
    393 			if (ex_run_str(sp, NULL, tp->lb, tp->len, 0, 1))
    394 				return (1);
    395 		}
    396 
    397 		/* Home the cursor. */
    398 		vs_home(sp);
    399 
    400 		/*
    401 		 * !!!
    402 		 * If the editor wrote the screen behind curses back, put out
    403 		 * a <newline> so that we don't overwrite the user's command
    404 		 * with its output or the next want-to-continue? message.  This
    405 		 * doesn't belong here, but I can't find another place to put
    406 		 * it.  See, we resolved the output from the last ex command,
    407 		 * and the user entered another one.  This is the only place
    408 		 * where we have control before the ex command writes output.
    409 		 * We could get control in vs_msg(), but we have no way to know
    410 		 * if command didn't put out any output when we try and resolve
    411 		 * this command.  This fixes a bug where combinations of ex
    412 		 * commands, e.g. ":set<CR>:!date<CR>:set" didn't look right.
    413 		 */
    414 		if (F_ISSET(sp, SC_SCR_EXWROTE))
    415 			(void)putchar('\n');
    416 
    417 		/* Call the ex parser. */
    418 		(void)ex_cmd(sp);
    419 
    420 		/* Flush ex messages. */
    421 		(void)ex_fflush(sp);
    422 
    423 		/* Resolve any messages. */
    424 		if (vs_ex_resolve(sp, &ifcontinue))
    425 			return (1);
    426 
    427 		/*
    428 		 * Continue or return.  If continuing, make sure that we
    429 		 * eventually do resolution.
    430 		 */
    431 		if (!ifcontinue)
    432 			break;
    433 		do_resolution = 1;
    434 
    435 		/* If we're continuing, it's a new command. */
    436 		++sp->ccnt;
    437 	}
    438 
    439 	/*
    440 	 * If the user previously continued an ex command, we have to do
    441 	 * resolution to clean up the screen.  Don't wait, we already did
    442 	 * that.
    443 	 */
    444 	if (do_resolution) {
    445 		F_SET(sp, SC_EX_WAIT_NO);
    446 		if (vs_ex_resolve(sp, &ifcontinue))
    447 			return (1);
    448 	}
    449 
    450 	/* Cleanup from the ex command. */
    451 	if (v_ex_done(sp, vp))
    452 		return (1);
    453 
    454 	/* The user may want to edit their colon command history. */
    455 	if (do_cedit)
    456 		return (v_ecl(sp));
    457 
    458 	return (0);
    459 }
    460 
    461 /*
    462  * v_ex_done --
    463  *	Cleanup from an ex command.
    464  */
    465 static int
    466 v_ex_done(SCR *sp, VICMD *vp)
    467 {
    468 	size_t len;
    469 
    470 	/*
    471 	 * The only cursor modifications are real, however, the underlying
    472 	 * line may have changed; don't trust anything.  This code has been
    473 	 * a remarkably fertile place for bugs.  Do a reality check on a
    474 	 * cursor value, and make sure it's okay.  If necessary, change it.
    475 	 * Ex keeps track of the line number, but it cares less about the
    476 	 * column and it may have disappeared.
    477 	 *
    478 	 * Don't trust ANYTHING.
    479 	 *
    480 	 * XXX
    481 	 * Ex will soon have to start handling the column correctly; see
    482 	 * the POSIX 1003.2 standard.
    483 	 */
    484 	if (db_eget(sp, sp->lno, NULL, &len, NULL)) {
    485 		sp->lno = 1;
    486 		sp->cno = 0;
    487 	} else if (sp->cno >= len)
    488 		sp->cno = len ? len - 1 : 0;
    489 
    490 	vp->m_final.lno = sp->lno;
    491 	vp->m_final.cno = sp->cno;
    492 
    493 	/*
    494 	 * Don't re-adjust the cursor after executing an ex command,
    495 	 * and ex movements are permanent.
    496 	 */
    497 	F_CLR(vp, VM_RCM_MASK);
    498 	F_SET(vp, VM_RCM_SET);
    499 
    500 	return (0);
    501 }
    502 
    503 /*
    504  * v_ecl --
    505  *	Start an edit window on the colon command-line commands.
    506  */
    507 static int
    508 v_ecl(SCR *sp)
    509 {
    510 	GS *gp;
    511 	WIN *wp;
    512 	SCR *new;
    513 
    514 	/* Initialize the screen, if necessary. */
    515 	gp = sp->gp;
    516 	wp = sp->wp;
    517 	if (wp->ccl_sp == NULL && v_ecl_init(sp))
    518 		return (1);
    519 
    520 	/* Get a new screen. */
    521 	if (screen_init(gp, sp, &new))
    522 		return (1);
    523 	if (vs_split(sp, new, 1)) {
    524 		(void)screen_fini(new);
    525 		return (1);
    526 	}
    527 
    528 	/* Attach to the screen. */
    529 	new->ep = wp->ccl_sp->ep;
    530 	++new->ep->refcnt;
    531 	TAILQ_INSERT_HEAD(&new->ep->scrq, new, eq);
    532 
    533 	new->frp = wp->ccl_sp->frp;
    534 	new->frp->flags = sp->frp->flags;
    535 	new->conv = wp->ccl_sp->conv;
    536 
    537 	/* Move the cursor to the end. */
    538 	(void)db_last(new, &new->lno);
    539 	if (new->lno == 0)
    540 		new->lno = 1;
    541 
    542 	/* Remember the originating window. */
    543 	sp->ccl_parent = sp;
    544 
    545 	/* It's a special window. */
    546 	F_SET(new, SC_COMEDIT);
    547 
    548 	/* Don't encode on writing to DB. */
    549 	o_set(new, O_FILEENCODING, OS_STRDUP, "WCHAR_T", 0);
    550 
    551 	/* Set up the switch. */
    552 	sp->nextdisp = new;
    553 	F_SET(sp, SC_SSWITCH);
    554 	return (0);
    555 }
    556 
    557 /*
    558  * v_ecl_exec --
    559  *	Execute a command from a colon command-line window.
    560  *
    561  * PUBLIC: int v_ecl_exec __P((SCR *));
    562  */
    563 int
    564 v_ecl_exec(SCR *sp)
    565 {
    566 	size_t len;
    567 	CHAR_T *p;
    568 
    569 	if (db_get(sp, sp->lno, 0, &p, &len) && sp->lno == 1) {
    570 		v_emsg(sp, NULL, VIM_EMPTY);
    571 		return (1);
    572 	}
    573 	if (len == 0) {
    574 		msgq(sp, M_BERR, "307|No ex command to execute");
    575 		return (1);
    576 	}
    577 
    578 	/* Push the command on the command stack. */
    579 	if (ex_run_str(sp, NULL, p, len, 0, 0))
    580 		return (1);
    581 
    582 	/* Set up the switch. */
    583 	sp->nextdisp = sp->ccl_parent;
    584 	F_SET(sp, SC_EXIT);
    585 	return (0);
    586 }
    587 
    588 /*
    589  * v_ecl_log --
    590  *	Log a command into the colon command-line log file.
    591  */
    592 static int
    593 v_ecl_log(SCR *sp, TEXT *tp)
    594 {
    595 	db_recno_t lno;
    596 	int rval;
    597 	CHAR_T *p;
    598 	size_t len;
    599 	SCR *ccl_sp;
    600 
    601 	/* Initialize the screen, if necessary. */
    602 	if (sp->wp->ccl_sp == NULL && v_ecl_init(sp))
    603 		return (1);
    604 
    605 	ccl_sp = sp->wp->ccl_sp;
    606 
    607 	/*
    608 	 * Don't log colon command window commands into the colon command
    609 	 * window...
    610 	 */
    611 	if (sp->ep == ccl_sp->ep)
    612 		return (0);
    613 
    614 	if (db_last(ccl_sp, &lno)) {
    615 		return (1);
    616 	}
    617 	/* Don't log line that is identical to previous one */
    618 	if (lno > 0 &&
    619 	    !db_get(ccl_sp, lno, 0, &p, &len) &&
    620 	    len == tp->len &&
    621 	    !MEMCMP(tp->lb, p, len))
    622 		rval = 0;
    623 	else {
    624 		rval = db_append(ccl_sp, 0, lno, tp->lb, tp->len);
    625 		/* XXXX end "transaction" on ccl */
    626 		/* Is this still necessary now that we no longer hijack sp ? */
    627 		log_cursor(ccl_sp);
    628 	}
    629 
    630 	return (rval);
    631 }
    632 
    633 /*
    634  * v_ecl_init --
    635  *	Initialize the colon command-line log file.
    636  */
    637 static int
    638 v_ecl_init(SCR *sp)
    639 {
    640 	FREF *frp;
    641 	GS *gp;
    642 	WIN *wp;
    643 
    644 	gp = sp->gp;
    645 	wp = sp->wp;
    646 
    647 	/* Get a temporary file. */
    648 	if ((frp = file_add(sp, NULL)) == NULL)
    649 		return (1);
    650 
    651 	/*
    652 	 * XXX
    653 	 * Create a screen -- the file initialization code wants one.
    654 	 */
    655 	if (screen_init(gp, sp, &wp->ccl_sp))
    656 		return (1);
    657 	conv_enc(wp->ccl_sp, O_FILEENCODING, "WCHAR_T");
    658 	if (file_init(wp->ccl_sp, frp, NULL, 0)) {
    659 		(void)screen_fini(wp->ccl_sp);
    660 		wp->ccl_sp = 0;
    661 		return (1);
    662 	}
    663 
    664 	/* The underlying file isn't recoverable. */
    665 	F_CLR(wp->ccl_sp->ep, F_RCV_ON);
    666 
    667 	return (0);
    668 }
    669