コード例 #1
0
ファイル: v_scroll.c プロジェクト: lichray/nvi2
/*
 * v_lgoto -- [count]G
 *	Go to first non-blank character of the line count, the last line
 *	of the file by default.
 *
 * PUBLIC: int v_lgoto(SCR *, VICMD *);
 */
int
v_lgoto(SCR *sp, VICMD *vp)
{
	recno_t nlines;

	if (F_ISSET(vp, VC_C1SET)) {
		if (!db_exist(sp, vp->count)) {
			/*
			 * !!!
			 * Historically, 1G was legal in an empty file.
			 */
			if (vp->count == 1) {
				if (db_last(sp, &nlines))
					return (1);
				if (nlines == 0)
					return (0);
			}
			v_eof(sp, &vp->m_start);
			return (1);
		}
		vp->m_stop.lno = vp->count;
	} else {
		if (db_last(sp, &nlines))
			return (1);
		vp->m_stop.lno = nlines ? nlines : 1;
	}
	goto_adjust(vp);
	return (0);
}
コード例 #2
0
ファイル: ex_equal.c プロジェクト: fishman/nvi
/*
 * ex_equal -- :address =
 *
 * PUBLIC: int ex_equal __P((SCR *, EXCMD *));
 */
int
ex_equal(SCR *sp, EXCMD *cmdp)
{
	db_recno_t lno;

	NEEDFILE(sp, cmdp);

	/*
	 * Print out the line number matching the specified address,
	 * or the number of the last line in the file if no address
	 * specified.
	 *
	 * !!!
	 * Historically, ":0=" displayed 0, and ":=" or ":1=" in an
	 * empty file displayed 1.  Until somebody complains loudly,
	 * we're going to do it right.  The tables in excmd.c permit
	 * lno to get away with any address from 0 to the end of the
	 * file, which, in an empty file, is 0.
	 */
	if (F_ISSET(cmdp, E_ADDR_DEF)) {
		if (db_last(sp, &lno))
			return (1);
	} else
		lno = cmdp->addr1.lno;

	(void)ex_printf(sp, "%ld\n", lno);
	return (0);
}
コード例 #3
0
/*
 * v_cfirst -- [count]_
 *	Move to the first non-blank character in a line.
 *
 * PUBLIC: int v_cfirst __P((SCR *, VICMD *));
 */
int
v_cfirst(SCR *sp, VICMD *vp)
{
	db_recno_t cnt, lno;

	/*
	 * !!!
	 * If the _ is a motion component, it makes the command a line motion
	 * e.g. "d_" deletes the line.  It also means that the cursor doesn't
	 * move.
	 *
	 * The _ command never failed in the first column.
	 */
	if (ISMOTION(vp))
		F_SET(vp, VM_LMODE);
	/*
	 * !!!
	 * Historically a specified count makes _ move down count - 1
	 * rows, so, "3_" is the same as "2j_".
	 */
	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
	if (cnt != 1) {
		--vp->count;
		return (v_down(sp, vp));
	}

	/*
	 * Move to the first non-blank.
	 *
	 * Can't just use RCM_SET_FNB, in case _ is used as the motion
	 * component of another command.
	 */
	vp->m_stop.cno = 0;
	if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
		return (1);

	/*
	 * !!!
	 * The _ command has to fail if the file is empty and we're doing
	 * a delete.  If deleting line 1, and 0 is the first nonblank,
	 * make the check.
	 */
	if (vp->m_stop.lno == 1 &&
	    vp->m_stop.cno == 0 && ISCMD(vp->rkp, 'd')) {
		if (db_last(sp, &lno))
			return (1);
		if (lno == 0) {
			v_sol(sp);
			return (1);
		}
	}

	/*
	 * Delete and non-motion commands move to the end of the range,
	 * yank stays at the start.  Ignore others.
	 */
	vp->m_final =
	    ISMOTION(vp) && ISCMD(vp->rkp, 'y') ? vp->m_start : vp->m_stop;
	return (0);
}
コード例 #4
0
ファイル: ex_delete.c プロジェクト: Alkzndr/freebsd
/*
 * ex_delete: [line [,line]] d[elete] [buffer] [count] [flags]
 *
 *	Delete lines from the file.
 *
 * PUBLIC: int ex_delete __P((SCR *, EXCMD *));
 */
int
ex_delete(SCR *sp, EXCMD *cmdp)
{
	recno_t lno;

	NEEDFILE(sp, cmdp);

	/*
	 * !!!
	 * Historically, lines deleted in ex were not placed in the numeric
	 * buffers.  We follow historic practice so that we don't overwrite
	 * vi buffers accidentally.
	 */
	if (cut(sp,
	    FL_ISSET(cmdp->iflags, E_C_BUFFER) ? &cmdp->buffer : NULL,
	    &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
		return (1);

	/* Delete the lines. */
	if (del(sp, &cmdp->addr1, &cmdp->addr2, 1))
		return (1);

	/* Set the cursor to the line after the last line deleted. */
	sp->lno = cmdp->addr1.lno;

	/* Or the last line in the file if deleted to the end of the file. */
	if (db_last(sp, &lno))
		return (1);
	if (sp->lno > lno)
		sp->lno = lno;
	return (0);
}
コード例 #5
0
ファイル: api.c プロジェクト: AgamAgarwal/minix
/*
 * api_extend --
 *	Extend file.
 *
 * PUBLIC: int api_extend __P((SCR *, db_recno_t));
 */
int 
api_extend(SCR *sp, db_recno_t lno)
{
	db_recno_t lastlno;
	if (db_last(sp, &lastlno))
	    return 1;
	while(lastlno < lno)
	    if (db_append(sp, 1, lastlno++, NULL, 0))
		return 1;
	return 0;
}
コード例 #6
0
/*
 * v_write --
 *	Write command.
 */
static int
v_write(SCR *sp, VICMD *vp)
{
	EXCMD cmd;

	ex_cinit(sp, &cmd, C_WRITE, 0, OOBLNO, OOBLNO, 0);

	cmd.addr1.lno = 1;
	if (db_last(sp, &cmd.addr2.lno))
		return (1);
	return (v_exec_ex(sp, vp, &cmd));
}
コード例 #7
0
/*
 * rcv_init --
 *	Force the file to be snapshotted for recovery.
 *
 * PUBLIC: int rcv_init __P((SCR *));
 */
int
rcv_init(SCR *sp)
{
	EXF *ep;
	db_recno_t lno;

	ep = sp->ep;

	/* Only do this once. */
	F_CLR(ep, F_FIRSTMODIFY);

	/* If we already know the file isn't recoverable, we're done. */
	if (!F_ISSET(ep, F_RCV_ON))
		return (0);

	/* Turn off recoverability until we figure out if this will work. */
	F_CLR(ep, F_RCV_ON);

	/* Test if we're recovering a file, not editing one. */
	if (ep->rcv_mpath == NULL) {
		/* Build a file to mail to the user. */
		if (rcv_mailfile(sp, 0, NULL))
			goto err;

		/* Force a read of the entire file. */
		if (db_last(sp, &lno))
			goto err;

		/* Turn on a busy message, and sync it to backing store. */
		sp->gp->scr_busy(sp,
		    "057|Copying file for recovery...", BUSY_ON);
		if (ep->db->sync(ep->db, 0)) {
			msgq_str(sp, M_SYSERR, ep->rcv_path,
			    "058|Preservation failed: %s");
			sp->gp->scr_busy(sp, NULL, BUSY_OFF);
			goto err;
		}
		sp->gp->scr_busy(sp, NULL, BUSY_OFF);
	}

	/* Turn off the owner execute bit. */
	(void)chmod(ep->rcv_path, S_IRUSR | S_IWUSR);

	/* We believe the file is recoverable. */
	F_SET(ep, F_RCV_ON);
	return (0);

err:	msgq(sp, M_ERR,
	    "059|Modifications not recoverable if the session fails");
	return (1);
}
コード例 #8
0
ファイル: v_util.c プロジェクト: Alkzndr/freebsd
/*
 * v_eof --
 *	Vi end-of-file error.
 *
 * PUBLIC: void v_eof __P((SCR *, MARK *));
 */
void
v_eof(SCR *sp, MARK *mp)
{
	recno_t lno;

	if (mp == NULL)
		v_emsg(sp, NULL, VIM_EOF);
	else {
		if (db_last(sp, &lno))
			return;
		if (mp->lno >= lno)
			v_emsg(sp, NULL, VIM_EOF);
		else
			msgq(sp, M_BERR, "195|Movement past the end-of-file");
	}
}
コード例 #9
0
static int
io(SCR *sp, VICMD *vp, enum which cmd)
{
	db_recno_t ai_line, lno;
	size_t len;
	u_int32_t flags;
	CHAR_T *p;

	flags = set_txt_std(sp, vp, TXT_ADDNEWLINE | TXT_APPENDEOL);
	sp->showmode = SM_INSERT;

	if (sp->lno == 1) {
		if (db_last(sp, &lno))
			return (1);
		if (lno != 0)
			goto insert;
		p = NULL;
		len = 0;
		ai_line = OOBLNO;
	} else {
		static CHAR_T nul = 0;
insert:		p = &nul;
		sp->cno = 0;
		LOG_CORRECT;

		if (cmd == O_cmd) {
			if (db_insert(sp, sp->lno, p, 0))
				return (1);
			if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
				return (1);
			ai_line = sp->lno + 1;
		} else {
			if (db_append(sp, 1, sp->lno, p, 0))
				return (1);
			if (db_get(sp, ++sp->lno, DBG_FATAL, &p, &len))
				return (1);
			ai_line = sp->lno - 1;
		}
	}
	return (v_txt(sp, vp, NULL, p, len,
	    0, ai_line, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
}
コード例 #10
0
ファイル: ip_funcs.c プロジェクト: AgamAgarwal/minix
/*
 * ip_refresh --
 *	Refresh the screen.
 *
 * PUBLIC: int ip_refresh __P((SCR *, int));
 */
int
ip_refresh(SCR *sp, int repaint)
{
	IP_BUF ipb;
	IP_PRIVATE *ipp;
	db_recno_t total;

	ipp = IPP(sp);

	/*
	 * If the scroll bar information has changed since we last sent
	 * it, resend it.  Currently, we send three values:
	 *
	 * top		The line number of the first line in the screen.
	 * num		The number of lines visible on the screen.
	 * total	The number of lines in the file.
	 *
	 * XXX
	 * This is a gross violation of layering... we're looking at data
	 * structures at which we have absolutely no business whatsoever
	 * looking...
	 */
	ipb.val1 = HMAP->lno;
	ipb.val2 = TMAP->lno - HMAP->lno;
	if (sp->ep != NULL && sp->ep->db != NULL)
		(void)db_last(sp, &total);
	ipb.val3 = total == 0 ? 1 : total;
	if (ipb.val1 != ipp->sb_top ||
	    ipb.val2 != ipp->sb_num || ipb.val3 != ipp->sb_total) {
		ipb.code = SI_SCROLLBAR;
		(void)vi_send(ipp->o_fd, "123", &ipb);
		ipp->sb_top = ipb.val1;
		ipp->sb_num = ipb.val2;
		ipp->sb_total = ipb.val3;
	}

	/* Refresh/repaint the screen. */
	ipb.code = repaint ? SI_REDRAW : SI_REFRESH;
	return (vi_send(ipp->o_fd, NULL, &ipb));
}
コード例 #11
0
/*
 * db_eget --
 *	Front-end to db_get, special case handling for empty files.
 *
 * PUBLIC: int db_eget __P((SCR *, db_recno_t, CHAR_T **, size_t *, int *));
 */
int
db_eget(SCR *sp, db_recno_t lno, CHAR_T **pp, size_t *lenp, int *isemptyp)
	        
	               				/* Line number. */
	            				/* Pointer store. */
	             				/* Length store. */
	              
{
	db_recno_t l1;

	if (isemptyp != NULL)
		*isemptyp = 0;

	/* If the line exists, simply return it. */
	if (!db_get(sp, lno, 0, pp, lenp))
		return (0);

	/*
	 * If the user asked for line 0 or line 1, i.e. the only possible
	 * line in an empty file, find the last line of the file; db_last
	 * fails loudly.
	 */
	if ((lno == 0 || lno == 1) && db_last(sp, &l1))
		return (1);

	/* If the file isn't empty, fail loudly. */
	if ((lno != 0 && lno != 1) || l1 != 0) {
		db_err(sp, lno);
		return (1);
	}

	if (isemptyp != NULL)
		*isemptyp = 1;

	return (1);
}
コード例 #12
0
/*
 * sscr_exec --
 *	Take a line and hand it off to the shell.
 *
 * PUBLIC: int sscr_exec __P((SCR *, db_recno_t));
 */
int
sscr_exec(SCR *sp, db_recno_t lno)
{
	SCRIPT *sc;
	db_recno_t last_lno;
	size_t blen, len, last_len;
	int isempty, matchprompt, rval;
	ssize_t nw;
	char *bp = NULL;
	const char *p;
	const CHAR_T *ip;
	size_t ilen;

	sc = sp->script;

	/* If there's a prompt on the last line, append the command. */
	if (db_last(sp, &last_lno))
		return (1);
	if (db_get(sp, last_lno, DBG_FATAL, __UNCONST(&ip), &ilen))
		return (1);
	INT2CHAR(sp, ip, ilen, p, last_len);
	if (last_len == sc->sh_prompt_len &&
	    memcmp(p, sc->sh_prompt, last_len) == 0) {
		matchprompt = 1;
		GET_SPACE_RETC(sp, bp, blen, last_len + 128);
		memmove(bp, p, last_len);
	} else
		matchprompt = 0;

	/* Get something to execute. */
	if (db_eget(sp, lno, __UNCONST(&ip), &ilen, &isempty)) {
		if (isempty)
			goto empty;
		goto err1;
	}

	/* Empty lines aren't interesting. */
	if (ilen == 0)
		goto empty;
	INT2CHAR(sp, ip, ilen, p, len);

	/* Delete any prompt. */
	if (len >= sc->sh_prompt_len &&
	    memcmp(p, sc->sh_prompt, sc->sh_prompt_len) == 0) {
		len -= sc->sh_prompt_len;
		if (len == 0) {
empty:			msgq(sp, M_BERR, "151|No command to execute");
			goto err1;
		}
		p += sc->sh_prompt_len;
	}

	/* Push the line to the shell. */
	if ((size_t)(nw = write(sc->sh_master, p, len)) != len)
		goto err2;
	rval = 0;
	if (write(sc->sh_master, "\n", 1) != 1) {
err2:		if (nw == 0)
			errno = EIO;
		msgq(sp, M_SYSERR, "shell");
		goto err1;
	}

	if (matchprompt) {
		ADD_SPACE_GOTO(sp, char, bp, blen, last_len + len);
		memmove(bp + last_len, p, len);
		CHAR2INT(sp, bp, last_len + len, ip, ilen);
		if (db_set(sp, last_lno, ip, ilen))
err1:			rval = 1;
	}
	if (matchprompt)
alloc_err:	FREE_SPACE(sp, bp, blen);
	return (rval);
}
コード例 #13
0
/*
 * put --
 *	Put text buffer contents into the file.
 *
 * PUBLIC: int put __P((SCR *, CB *, CHAR_T *, MARK *, MARK *, int));
 */
int
put(SCR *sp, CB *cbp, ARG_CHAR_T *namep, MARK *cp, MARK *rp, int append)
{
	ARG_CHAR_T name;
	TEXT *ltp, *tp;
	db_recno_t lno;
	size_t blen, clen, len;
	int rval;
	CHAR_T *bp, *t;
	CHAR_T *p;

	if (cbp == NULL) {
		if (namep == NULL) {
			cbp = sp->wp->dcbp;
			if (cbp == NULL) {
				msgq(sp, M_ERR,
				    "053|The default buffer is empty");
				return (1);
			}
		} else {
			name = *namep;
			CBNAME(sp, cbp, name);
			if (cbp == NULL) {
				msgq(sp, M_ERR, "054|Buffer %s is empty",
				    KEY_NAME(sp, name));
				return (1);
			}
		}
	}
	tp = cbp->textq.cqh_first;

	/*
	 * It's possible to do a put into an empty file, meaning that the cut
	 * buffer simply becomes the file.  It's a special case so that we can
	 * ignore it in general.
	 *
	 * !!!
	 * Historically, pasting into a file with no lines in vi would preserve
	 * the single blank line.  This is surely a result of the fact that the
	 * historic vi couldn't deal with a file that had no lines in it.  This
	 * implementation treats that as a bug, and does not retain the blank
	 * line.
	 *
	 * Historical practice is that the cursor ends at the first character
	 * in the file.
	 */
	if (cp->lno == 1) {
		if (db_last(sp, &lno))
			return (1);
		if (lno == 0) {
			for (; tp != (void *)&cbp->textq;
			    ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
				if (db_append(sp, 1, lno, tp->lb, tp->len))
					return (1);
			rp->lno = 1;
			rp->cno = 0;
			return (0);
		}
	}

	/* If a line mode buffer, append each new line into the file. */
	if (F_ISSET(cbp, CB_LMODE)) {
		lno = append ? cp->lno : cp->lno - 1;
		rp->lno = lno + 1;
		for (; tp != (void *)&cbp->textq;
		    ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
			if (db_append(sp, 1, lno, tp->lb, tp->len))
				return (1);
		rp->cno = 0;
		(void)nonblank(sp, rp->lno, &rp->cno);
		return (0);
	}

	/*
	 * If buffer was cut in character mode, replace the current line with
	 * one built from the portion of the first line to the left of the
	 * split plus the first line in the CB.  Append each intermediate line
	 * in the CB.  Append a line built from the portion of the first line
	 * to the right of the split plus the last line in the CB.
	 *
	 * Get the first line.
	 */
	lno = cp->lno;
	if (db_get(sp, lno, DBG_FATAL, &p, &len))
		return (1);

	GET_SPACE_RETW(sp, bp, blen, tp->len + len + 1);
	t = bp;

	/* Original line, left of the split. */
	if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) {
		MEMCPYW(bp, p, clen);
		p += clen;
		t += clen;
	}

	/* First line from the CB. */
	if (tp->len != 0) {
		MEMCPYW(t, tp->lb, tp->len);
		t += tp->len;
	}

	/* Calculate length left in the original line. */
	clen = len == 0 ? 0 : len - (cp->cno + (append ? 1 : 0));

	/*
	 * !!!
	 * In the historical 4BSD version of vi, character mode puts within
	 * a single line have two cursor behaviors: if the put is from the
	 * unnamed buffer, the cursor moves to the character inserted which
	 * appears last in the file.  If the put is from a named buffer,
	 * the cursor moves to the character inserted which appears first
	 * in the file.  In System III/V, it was changed at some point and
	 * the cursor always moves to the first character.  In both versions
	 * of vi, character mode puts that cross line boundaries leave the
	 * cursor on the first character.  Nvi implements the System III/V
	 * behavior, and expect POSIX.2 to do so as well.
	 */
	rp->lno = lno;
	rp->cno = len == 0 ? 0 : sp->cno + (append && tp->len ? 1 : 0);

	/*
	 * If no more lines in the CB, append the rest of the original
	 * line and quit.  Otherwise, build the last line before doing
	 * the intermediate lines, because the line changes will lose
	 * the cached line.
	 */
	if (tp->q.cqe_next == (void *)&cbp->textq) {
		if (clen > 0) {
			MEMCPYW(t, p, clen);
			t += clen;
		}
		if (db_set(sp, lno, bp, t - bp))
			goto err;
		if (sp->rptlchange != lno) {
			sp->rptlchange = lno;
			++sp->rptlines[L_CHANGED];
		}
	} else {
		/*
		 * Have to build both the first and last lines of the
		 * put before doing any sets or we'll lose the cached
		 * line.  Build both the first and last lines in the
		 * same buffer, so we don't have to have another buffer
		 * floating around.
		 *
		 * Last part of original line; check for space, reset
		 * the pointer into the buffer.
		 */
		ltp = cbp->textq.cqh_last;
		len = t - bp;
		ADD_SPACE_RETW(sp, bp, blen, ltp->len + clen);
		t = bp + len;

		/* Add in last part of the CB. */
		MEMCPYW(t, ltp->lb, ltp->len);
		if (clen)
			MEMCPYW(t + ltp->len, p, clen);
		clen += ltp->len;

		/*
		 * Now: bp points to the first character of the first
		 * line, t points to the last character of the last
		 * line, t - bp is the length of the first line, and
		 * clen is the length of the last.  Just figured you'd
		 * want to know.
		 *
		 * Output the line replacing the original line.
		 */
		if (db_set(sp, lno, bp, t - bp))
			goto err;
		if (sp->rptlchange != lno) {
			sp->rptlchange = lno;
			++sp->rptlines[L_CHANGED];
		}

		/* Output any intermediate lines in the CB. */
		for (tp = tp->q.cqe_next;
		    tp->q.cqe_next != (void *)&cbp->textq;
		    ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
			if (db_append(sp, 1, lno, tp->lb, tp->len))
				goto err;

		if (db_append(sp, 1, lno, t, clen))
			goto err;
		++sp->rptlines[L_ADDED];
	}
	rval = 0;

	if (0)
err:		rval = 1;

	FREE_SPACEW(sp, bp, blen);
	return (rval);
}
コード例 #14
0
ファイル: api.c プロジェクト: AgamAgarwal/minix
/*
 * api_lline --
 *	Return the line number of the last line in the file.
 *
 * PUBLIC: int api_lline __P((SCR *, db_recno_t *));
 */
int
api_lline(SCR *sp, db_recno_t *lnop)
{
	return (db_last(sp, lnop));
}
コード例 #15
0
ファイル: search.c プロジェクト: Alkzndr/freebsd
/*
 * search_init --
 *	Set up a search.
 */
static int
search_init(
	SCR *sp,
	dir_t dir,
	CHAR_T *ptrn,
	size_t plen,
	CHAR_T **epp,
	u_int flags)
{
	recno_t lno;
	int delim;
	CHAR_T *p, *t;

	/* If the file is empty, it's a fast search. */
	if (sp->lno <= 1) {
		if (db_last(sp, &lno))
			return (1);
		if (lno == 0) {
			if (LF_ISSET(SEARCH_MSG))
				search_msg(sp, S_EMPTY);
			return (1);
		}
	}

	if (LF_ISSET(SEARCH_PARSE)) {		/* Parse the string. */
		/*
		 * Use the saved pattern if no pattern specified, or if only
		 * one or two delimiter characters specified.
		 *
		 * !!!
		 * Historically, only the pattern itself was saved, vi didn't
		 * preserve addressing or delta information.
		 */
		if (ptrn == NULL)
			goto prev;
		if (plen == 1) {
			if (epp != NULL)
				*epp = ptrn + 1;
			goto prev;
		}
		if (ptrn[0] == ptrn[1]) {
			if (epp != NULL)
				*epp = ptrn + 2;

			/* Complain if we don't have a previous pattern. */
prev:			if (sp->re == NULL) {
				search_msg(sp, S_NOPREV);
				return (1);
			}
			/* Re-compile the search pattern if necessary. */
			if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp,
			    sp->re, sp->re_len, NULL, NULL, &sp->re_c,
			    RE_C_SEARCH |
			    (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT)))
				return (1);

			/* Set the search direction. */
			if (LF_ISSET(SEARCH_SET))
				sp->searchdir = dir;
			return (0);
		}

		/*
		 * Set the delimiter, and move forward to the terminating
		 * delimiter, handling escaped delimiters.
		 *
		 * QUOTING NOTE:
		 * Only discard an escape character if it escapes a delimiter.
		 */
		for (delim = *ptrn, p = t = ++ptrn;; *t++ = *p++) {
			if (--plen == 0 || p[0] == delim) {
				if (plen != 0)
					++p;
				break;
			}
			if (plen > 1 && p[0] == '\\' && p[1] == delim) {
				++p;
				--plen;
			}
		}
		if (epp != NULL)
			*epp = p;

		plen = t - ptrn;
	}

	/* Compile the RE. */
	if (re_compile(sp, ptrn, plen, &sp->re, &sp->re_len, &sp->re_c,
	    RE_C_SEARCH |
	    (LF_ISSET(SEARCH_MSG) ? 0 : RE_C_SILENT) |
	    (LF_ISSET(SEARCH_TAG) ? RE_C_TAG : 0) |
	    (LF_ISSET(SEARCH_CSCOPE) ? RE_C_CSCOPE : 0)))
		return (1);

	/* Set the search direction. */
	if (LF_ISSET(SEARCH_SET))
		sp->searchdir = dir;

	return (0);
}
コード例 #16
0
ファイル: delete.c プロジェクト: Alkzndr/freebsd
/*
 * del --
 *	Delete a range of text.
 *
 * PUBLIC: int del __P((SCR *, MARK *, MARK *, int));
 */
int
del(
	SCR *sp,
	MARK *fm,
	MARK *tm,
	int lmode)
{
	recno_t lno;
	size_t blen, len, nlen, tlen;
	CHAR_T *bp, *p;
	int eof, rval;

	bp = NULL;

	/* Case 1 -- delete in line mode. */
	if (lmode) {
		for (lno = tm->lno; lno >= fm->lno; --lno) {
			if (db_delete(sp, lno))
				return (1);
			++sp->rptlines[L_DELETED];
			if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
				break;
		}
		goto done;
	}

	/*
	 * Case 2 -- delete to EOF.  This is a special case because it's
	 * easier to pick it off than try and find it in the other cases.
 	 */
	if (db_last(sp, &lno))
		return (1);
	if (tm->lno >= lno) {
		if (tm->lno == lno) {
			if (db_get(sp, lno, DBG_FATAL, &p, &len))
				return (1);
			eof = tm->cno != ENTIRE_LINE && tm->cno >= len ? 1 : 0;
		} else
			eof = 1;
		if (eof) {
			for (lno = tm->lno; lno > fm->lno; --lno) {
				if (db_delete(sp, lno))
					return (1);
				++sp->rptlines[L_DELETED];
				if (lno %
				    INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
					break;
			}
			if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
				return (1);
			GET_SPACE_RETW(sp, bp, blen, fm->cno);
			MEMCPY(bp, p, fm->cno);
			if (db_set(sp, fm->lno, bp, fm->cno))
				return (1);
			goto done;
		}
	}

	/* Case 3 -- delete within a single line. */
	if (tm->lno == fm->lno) {
		if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
			return (1);
		GET_SPACE_RETW(sp, bp, blen, len);
		if (fm->cno != 0)
			MEMCPY(bp, p, fm->cno);
		MEMCPY(bp + fm->cno, p + (tm->cno + 1), 
			len - (tm->cno + 1));
		if (db_set(sp, fm->lno,
		    bp, len - ((tm->cno - fm->cno) + 1)))
			goto err;
		goto done;
	}

	/*
	 * Case 4 -- delete over multiple lines.
	 *
	 * Copy the start partial line into place.
	 */
	if ((tlen = fm->cno) != 0) {
		if (db_get(sp, fm->lno, DBG_FATAL, &p, NULL))
			return (1);
		GET_SPACE_RETW(sp, bp, blen, tlen + 256);
		MEMCPY(bp, p, tlen);
	}

	/* Copy the end partial line into place. */
	if (db_get(sp, tm->lno, DBG_FATAL, &p, &len))
		goto err;
	if (len != 0 && tm->cno != len - 1) {
		/*
		 * XXX
		 * We can overflow memory here, if the total length is greater
		 * than SIZE_T_MAX.  The only portable way I've found to test
		 * is depending on the overflow being less than the value.
		 */
		nlen = (len - (tm->cno + 1)) + tlen;
		if (tlen > nlen) {
			msgq(sp, M_ERR, "002|Line length overflow");
			goto err;
		}
		if (tlen == 0) {
			GET_SPACE_RETW(sp, bp, blen, nlen);
		} else
			ADD_SPACE_RETW(sp, bp, blen, nlen);

		MEMCPY(bp + tlen, p + (tm->cno + 1), len - (tm->cno + 1));
		tlen += len - (tm->cno + 1);
	}

	/* Set the current line. */
	if (db_set(sp, fm->lno, bp, tlen))
		goto err;

	/* Delete the last and intermediate lines. */
	for (lno = tm->lno; lno > fm->lno; --lno) {
		if (db_delete(sp, lno))
			goto err;
		++sp->rptlines[L_DELETED];
		if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
			break;
	}

done:	rval = 0;
	if (0)
err:		rval = 1;
	if (bp != NULL)
		FREE_SPACEW(sp, bp, blen);
	return (rval);
}
コード例 #17
0
/*
 * sscr_insert --
 *	Take a line from the shell and insert it into the file.
 */
static int
sscr_insert(SCR *sp)
{
	struct timeval tv;
	char *endp, *p, *t;
	SCRIPT *sc;
	fd_set rdfd;
	db_recno_t lno;
	size_t len;
	ssize_t nr;
	char bp[1024];
	const CHAR_T *ip;
	size_t ilen = 0;

	/* Find out where the end of the file is. */
	if (db_last(sp, &lno))
		return (1);

	endp = bp;

	/* Read the characters. */
	sc = sp->script;
more:	switch (nr = read(sc->sh_master, endp, bp + sizeof(bp) - endp)) {
	case  0:			/* EOF; shell just exited. */
		sscr_end(sp);
		return (0);
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		return (1);
	default:
		endp += nr;
		break;
	}

	/* Append the lines into the file. */
	for (p = t = bp; p < endp; ++p) {
		if (*p == '\r' || *p == '\n') {
			len = p - t;
			if (CHAR2INT(sp, t, len, ip, ilen) ||
			    db_append(sp, 1, lno++, ip, ilen))
				return (1);
			t = p + 1;
		}
	}
	/*
	 * If the last thing from the shell isn't another prompt, wait up to
	 * 1/10 of a second for more stuff to show up, so that we don't break
	 * the output into two separate lines.  Don't want to hang indefinitely
	 * because some program is hanging, confused the shell, or whatever.
	 * Note that sc->sh_prompt can be NULL here.
	 */
	len = p - t;
	if (sc->sh_prompt == NULL || len != sc->sh_prompt_len ||
	    memcmp(t, sc->sh_prompt, len) != 0) {
		tv.tv_sec = 0;
		tv.tv_usec = 100000;
		FD_ZERO(&rdfd);
		FD_SET(sc->sh_master, &rdfd);
		if (select(sc->sh_master + 1, &rdfd, NULL, NULL, &tv) == 1) {
			if (len == sizeof(bp)) {
				if (CHAR2INT(sp, t, len, ip, ilen) ||
				    db_append(sp, 1, lno++, ip, ilen))
					return (1);
				endp = bp;
			} else {
				memmove(bp, t, len);
				endp = bp + len;
			}
			goto more;
		}
		if (sscr_setprompt(sp, t, len))
			return (1);
	}

	/* Append the remains into the file, and the cursor moves to EOF. */
	if (len > 0) {
		if (CHAR2INT(sp, t, len, ip, ilen) ||
		    db_append(sp, 1, lno++, ip, ilen))
			return (1);
		sp->cno = ilen - 1;
	} else
		sp->cno = 0;
	sp->lno = lno;
	return (vs_refresh(sp, 1));
}
コード例 #18
0
ファイル: ex_script.c プロジェクト: darksoul42/bitrig
/*
 * sscr_exec --
 *	Take a line and hand it off to the shell.
 *
 * PUBLIC: int sscr_exec(SCR *, recno_t);
 */
int
sscr_exec(SCR *sp, recno_t lno)
{
	SCRIPT *sc;
	recno_t last_lno;
	size_t blen, len, last_len, tlen;
	int isempty, matchprompt, nw, rval;
	char *bp, *p;

	/* If there's a prompt on the last line, append the command. */
	if (db_last(sp, &last_lno))
		return (1);
	if (db_get(sp, last_lno, DBG_FATAL, &p, &last_len))
		return (1);
	if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
		matchprompt = 1;
		GET_SPACE_RET(sp, bp, blen, last_len + 128);
		memmove(bp, p, last_len);
	} else
		matchprompt = 0;

	/* Get something to execute. */
	if (db_eget(sp, lno, &p, &len, &isempty)) {
		if (isempty)
			goto empty;
		goto err1;
	}

	/* Empty lines aren't interesting. */
	if (len == 0)
		goto empty;

	/* Delete any prompt. */
	if (sscr_matchprompt(sp, p, len, &tlen)) {
		if (tlen == len) {
empty:			msgq(sp, M_BERR, "No command to execute");
			goto err1;
		}
		p += (len - tlen);
		len = tlen;
	}

	/* Push the line to the shell. */
	sc = sp->script;
	if ((nw = write(sc->sh_master, p, len)) != len)
		goto err2;
	rval = 0;
	if (write(sc->sh_master, "\n", 1) != 1) {
err2:		if (nw == 0)
			errno = EIO;
		msgq(sp, M_SYSERR, "shell");
		goto err1;
	}

	if (matchprompt) {
		ADD_SPACE_RET(sp, bp, blen, last_len + len);
		memmove(bp + last_len, p, len);
		if (db_set(sp, last_lno, bp, last_len + len))
err1:			rval = 1;
	}
	if (matchprompt)
		FREE_SPACE(sp, bp, blen);
	return (rval);
}
コード例 #19
0
ファイル: exf.c プロジェクト: fishman/nvi
/*
 * file_write --
 *	Write the file to disk.  Historic vi had fairly convoluted
 *	semantics for whether or not writes would happen.  That's
 *	why all the flags.
 *
 * PUBLIC: int file_write __P((SCR *, MARK *, MARK *, char *, int));
 */
int
file_write(SCR *sp, MARK *fm, MARK *tm, char *name, int flags)
{
    enum { NEWFILE, OLDFILE } mtype;
    struct stat sb;
    EXF *ep;
    FILE *fp;
    FREF *frp;
    MARK from, to;
    size_t len;
    u_long nlno, nch;
    int fd, nf, noname, oflags, rval;
    char *p, *s, *t, buf[MAXPATHLEN + 64];
    const char *msgstr;

    ep = sp->ep;
    frp = sp->frp;

    /*
     * Writing '%', or naming the current file explicitly, has the
     * same semantics as writing without a name.
     */
    if (name == NULL || !strcmp(name, frp->name)) {
        noname = 1;
        name = frp->name;
    } else
        noname = 0;

    /* Can't write files marked read-only, unless forced. */
    if (!LF_ISSET(FS_FORCE) && noname && O_ISSET(sp, O_READONLY)) {
        msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
             "244|Read-only file, not written; use ! to override" :
             "245|Read-only file, not written");
        return (1);
    }

    /* If not forced, not appending, and "writeany" not set ... */
    if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) {
        /* Don't overwrite anything but the original file. */
        if ((!noname || F_ISSET(frp, FR_NAMECHANGE)) &&
                !stat(name, &sb)) {
            msgq_str(sp, M_ERR, name,
                     LF_ISSET(FS_POSSIBLE) ?
                     "246|%s exists, not written; use ! to override" :
                     "247|%s exists, not written");
            return (1);
        }

        /*
         * Don't write part of any existing file.  Only test for the
         * original file, the previous test catches anything else.
         */
        if (!LF_ISSET(FS_ALL) && noname && !stat(name, &sb)) {
            msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
                 "248|Partial file, not written; use ! to override" :
                 "249|Partial file, not written");
            return (1);
        }
    }

    /*
     * Figure out if the file already exists -- if it doesn't, we display
     * the "new file" message.  The stat might not be necessary, but we
     * just repeat it because it's easier than hacking the previous tests.
     * The information is only used for the user message and modification
     * time test, so we can ignore the obvious race condition.
     *
     * One final test.  If we're not forcing or appending the current file,
     * and we have a saved modification time, object if the file changed
     * since we last edited or wrote it, and make them force it.
     */
    if (stat(name, &sb))
        mtype = NEWFILE;
    else {
        if (noname && !LF_ISSET(FS_FORCE | FS_APPEND) &&
                ((F_ISSET(ep, F_DEVSET) &&
                  (sb.st_dev != ep->mdev || sb.st_ino != ep->minode)) ||
                 sb.st_mtime != ep->mtime)) {
            msgq_str(sp, M_ERR, name, LF_ISSET(FS_POSSIBLE) ?
                     "250|%s: file modified more recently than this copy; use ! to override" :
                     "251|%s: file modified more recently than this copy");
            return (1);
        }

        mtype = OLDFILE;
    }

    /* Set flags to create, write, and either append or truncate. */
    oflags = O_CREAT | O_WRONLY |
             (LF_ISSET(FS_APPEND) ? O_APPEND : O_TRUNC);

    /* Backup the file if requested. */
    if (!opts_empty(sp, O_BACKUP, 1) &&
            file_backup(sp, name, O_STR(sp, O_BACKUP)) && !LF_ISSET(FS_FORCE))
        return (1);

    /* Open the file. */
    SIGBLOCK;
    if ((fd = open(name, oflags,
                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) {
        msgq_str(sp, M_SYSERR, name, "%s");
        SIGUNBLOCK;
        return (1);
    }
    SIGUNBLOCK;

    /* Try and get a lock. */
    if (!noname && file_lock(sp, NULL, NULL, fd, 0) == LOCK_UNAVAIL)
        msgq_str(sp, M_ERR, name,
                 "252|%s: write lock was unavailable");

#if __linux__
    /*
     * XXX
     * In libc 4.5.x, fdopen(fd, "w") clears the O_APPEND flag (if set).
     * This bug is fixed in libc 4.6.x.
     *
     * This code works around this problem for libc 4.5.x users.
     * Note that this code is harmless if you're using libc 4.6.x.
     */
    if (LF_ISSET(FS_APPEND) && lseek(fd, (off_t)0, SEEK_END) < 0) {
        msgq(sp, M_SYSERR, name);
        return (1);
    }
#endif

    /*
     * Use stdio for buffering.
     *
     * XXX
     * SVR4.2 requires the fdopen mode exactly match the original open
     * mode, i.e. you have to open with "a" if appending.
     */
    if ((fp = fdopen(fd, LF_ISSET(FS_APPEND) ? "a" : "w")) == NULL) {
        msgq_str(sp, M_SYSERR, name, "%s");
        (void)close(fd);
        return (1);
    }

    /* Build fake addresses, if necessary. */
    if (fm == NULL) {
        from.lno = 1;
        from.cno = 0;
        fm = &from;
        if (db_last(sp, &to.lno))
            return (1);
        to.cno = 0;
        tm = &to;
    }

    rval = ex_writefp(sp, name, fp, fm, tm, &nlno, &nch, 0);

    /*
     * Save the new last modification time -- even if the write fails
     * we re-init the time.  That way the user can clean up the disk
     * and rewrite without having to force it.
     */
    if (noname) {
        if (stat(name, &sb))
            time(&ep->mtime);
        else {
            F_SET(ep, F_DEVSET);
            ep->mdev = sb.st_dev;
            ep->minode = sb.st_ino;

            ep->mtime = sb.st_mtime;
        }
    }

    /*
     * If the write failed, complain loudly.  ex_writefp() has already
     * complained about the actual error, reinforce it if data was lost.
     */
    if (rval) {
        if (!LF_ISSET(FS_APPEND))
            msgq_str(sp, M_ERR, name,
                     "254|%s: WARNING: FILE TRUNCATED");
        return (1);
    }

    /*
     * Once we've actually written the file, it doesn't matter that the
     * file name was changed -- if it was, we've already whacked it.
     */
    F_CLR(frp, FR_NAMECHANGE);

    /*
     * If wrote the entire file, and it wasn't by appending it to a file,
     * clear the modified bit.  If the file was written to the original
     * file name and the file is a temporary, set the "no exit" bit.  This
     * permits the user to write the file and use it in the context of the
     * filesystem, but still keeps them from discarding their changes by
     * exiting.
     */
    if (LF_ISSET(FS_ALL) && !LF_ISSET(FS_APPEND)) {
        F_CLR(ep, F_MODIFIED);
        if (F_ISSET(frp, FR_TMPFILE)) {
            if (noname)
                F_SET(frp, FR_TMPEXIT);
            else
                F_CLR(frp, FR_TMPEXIT);
        }
    }

    p = msg_print(sp, name, &nf);
    switch (mtype) {
    case NEWFILE:
        msgstr = msg_cat(sp,
                         "256|%s: new file: %lu lines, %lu characters", NULL);
        len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
        break;
    case OLDFILE:
        msgstr = msg_cat(sp, LF_ISSET(FS_APPEND) ?
                         "315|%s: appended: %lu lines, %lu characters" :
                         "257|%s: %lu lines, %lu characters", NULL);
        len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
        break;
    default:
        abort();
    }

    /*
     * There's a nasty problem with long path names.  Cscope and tags files
     * can result in long paths and vi will request a continuation key from
     * the user.  Unfortunately, the user has typed ahead, and chaos will
     * result.  If we assume that the characters in the filenames only take
     * a single screen column each, we can trim the filename.
     */
    s = buf;
    if (len >= sp->cols) {
        for (s = buf, t = buf + strlen(p); s < t &&
                (*s != '/' || len >= sp->cols - 3); ++s, --len);
        if (s == t)
            s = buf;
        else {
            *--s = '.';		/* Leading ellipses. */
            *--s = '.';
            *--s = '.';
        }
    }
    msgq(sp, M_INFO, s);
    if (nf)
        FREE_SPACE(sp, p, 0);
    return (0);
}
コード例 #20
0
ファイル: exf.c プロジェクト: fishman/nvi
/*
 * file_cinit --
 *	Set up the initial cursor position.
 */
static void
file_cinit(SCR *sp)
{
    GS *gp;
    MARK m;
    size_t len;
    int nb;
    CHAR_T *wp;
    size_t wlen;

    /* Set some basic defaults. */
    sp->lno = 1;
    sp->cno = 0;

    /*
     * Historically, initial commands (the -c option) weren't executed
     * until a file was loaded, e.g. "vi +10 nofile", followed by an
     * :edit or :tag command, would execute the +10 on the file loaded
     * by the subsequent command, (assuming that it existed).  This
     * applied as well to files loaded using the tag commands, and we
     * follow that historic practice.  Also, all initial commands were
     * ex commands and were always executed on the last line of the file.
     *
     * Otherwise, if no initial command for this file:
     *    If in ex mode, move to the last line, first nonblank character.
     *    If the file has previously been edited, move to the last known
     *	  position, and check it for validity.
     *    Otherwise, move to the first line, first nonblank.
     *
     * This gets called by the file init code, because we may be in a
     * file of ex commands and we want to execute them from the right
     * location in the file.
     */
    nb = 0;
    gp = sp->gp;
    if (gp->c_option != NULL && !F_ISSET(sp->frp, FR_NEWFILE)) {
        if (db_last(sp, &sp->lno))
            return;
        if (sp->lno == 0) {
            sp->lno = 1;
            sp->cno = 0;
        }
        CHAR2INT(sp, gp->c_option, strlen(gp->c_option) + 1,
                 wp, wlen);
        if (ex_run_str(sp, "-c option", wp, wlen - 1, 1, 1))
            return;
        gp->c_option = NULL;
    } else if (F_ISSET(sp, SC_EX)) {
        if (db_last(sp, &sp->lno))
            return;
        if (sp->lno == 0) {
            sp->lno = 1;
            sp->cno = 0;
            return;
        }
        nb = 1;
    } else {
        if (F_ISSET(sp->frp, FR_CURSORSET)) {
            sp->lno = sp->frp->lno;
            sp->cno = sp->frp->cno;

            /* If returning to a file in vi, center the line. */
            F_SET(sp, SC_SCR_CENTER);
        } else {
            if (O_ISSET(sp, O_COMMENT))
                file_comment(sp);
            else
                sp->lno = 1;
            nb = 1;
        }
        if (db_get(sp, sp->lno, 0, NULL, &len)) {
            sp->lno = 1;
            sp->cno = 0;
            return;
        }
        if (!nb && sp->cno > len)
            nb = 1;
    }
    if (nb) {
        sp->cno = 0;
        (void)nonblank(sp, sp->lno, &sp->cno);
    }

    /*
     * !!!
     * The initial column is also the most attractive column.
     */
    sp->rcm = sp->cno;

    /*
     * !!!
     * Historically, vi initialized the absolute mark, but ex did not.
     * Which meant, that if the first command in ex mode was "visual",
     * or if an ex command was executed first (e.g. vi +10 file) vi was
     * entered without the mark being initialized.  For consistency, if
     * the file isn't empty, we initialize it for everyone, believing
     * that it can't hurt, and is generally useful.  Not initializing it
     * if the file is empty is historic practice, although it has always
     * been possible to set (and use) marks in empty vi files.
     */
    m.lno = sp->lno;
    m.cno = sp->cno;
    (void)mark_set(sp, ABSMARK1, &m, 0);
}
コード例 #21
0
ファイル: ex_bang.c プロジェクト: lichray/nvi2
/*
 * ex_bang -- :[line [,line]] ! command
 *
 * Pass the rest of the line after the ! character to the program named by
 * the O_SHELL option.
 *
 * Historical vi did NOT do shell expansion on the arguments before passing
 * them, only file name expansion.  This means that the O_SHELL program got
 * "$t" as an argument if that is what the user entered.  Also, there's a
 * special expansion done for the bang command.  Any exclamation points in
 * the user's argument are replaced by the last, expanded ! command.
 *
 * There's some fairly amazing slop in this routine to make the different
 * ways of getting here display the right things.  It took a long time to
 * get it right (wrong?), so be careful.
 *
 * PUBLIC: int ex_bang(SCR *, EXCMD *);
 */
int
ex_bang(SCR *sp, EXCMD *cmdp)
{
	enum filtertype ftype;
	ARGS *ap;
	EX_PRIVATE *exp;
	MARK rm;
	recno_t lno;
	int rval;
	const char *msg;
	char *np;
	size_t nlen;

	ap = cmdp->argv[0];
	if (ap->len == 0) {
		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
		return (1);
	}

	/* Set the "last bang command" remembered value. */
	exp = EXP(sp);
	free(exp->lastbcomm);
	if ((exp->lastbcomm = v_wstrdup(sp, ap->bp, ap->len)) == NULL) {
		msgq(sp, M_SYSERR, NULL);
		return (1);
	}

	/*
	 * If the command was modified by the expansion, it was historically
	 * redisplayed.
	 */
	if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, SC_EX_SILENT)) {
		/*
		 * Display the command if modified.  Historic ex/vi displayed
		 * the command if it was modified due to file name and/or bang
		 * expansion.  If piping lines in vi, it would be immediately
		 * overwritten by any error or line change reporting.
		 */
		if (F_ISSET(sp, SC_VI))
			vs_update(sp, "!", ap->bp);
		else {
			(void)ex_printf(sp, "!"WS"\n", ap->bp);
			(void)ex_fflush(sp);
		}
	}

	/*
	 * If no addresses were specified, run the command.  If there's an
	 * underlying file, it's been modified and autowrite is set, write
	 * the file back.  If the file has been modified, autowrite is not
	 * set and the warn option is set, tell the user about the file.
	 */
	if (cmdp->addrcnt == 0) {
		msg = NULL;
		if (sp->ep != NULL && F_ISSET(sp->ep, F_MODIFIED))
			if (O_ISSET(sp, O_AUTOWRITE)) {
				if (file_aw(sp, FS_ALL))
					return (0);
			} else if (O_ISSET(sp, O_WARN) &&
			    !F_ISSET(sp, SC_EX_SILENT))
				msg = msg_cat(sp,
				    "303|File modified since last write.",
				    NULL);

		/* If we're still in a vi screen, move out explicitly. */
		INT2CHAR(sp, ap->bp, ap->len+1, np, nlen);
		(void)ex_exec_proc(sp,
		    cmdp, np, msg, !F_ISSET(sp, SC_EX | SC_SCR_EXWROTE));
	}

	/*
	 * If addresses were specified, pipe lines from the file through the
	 * command.
	 *
	 * Historically, vi lines were replaced by both the stdout and stderr
	 * lines of the command, but ex lines by only the stdout lines.  This
	 * makes no sense to me, so nvi makes it consistent for both, and
	 * matches vi's historic behavior.
	 */
	else {
		NEEDFILE(sp, cmdp);

		/* Autoprint is set historically, even if the command fails. */
		F_SET(cmdp, E_AUTOPRINT);

		/*
		 * !!!
		 * Historical vi permitted "!!" in an empty file.  When this
		 * happens, we arrive here with two addresses of 1,1 and a
		 * bad attitude.  The simple solution is to turn it into a
		 * FILTER_READ operation, with the exception that stdin isn't
		 * opened for the utility, and the cursor position isn't the
		 * same.  The only historic glitch (I think) is that we don't
		 * put an empty line into the default cut buffer, as historic
		 * vi did.  Imagine, if you can, my disappointment.
		 */
		ftype = FILTER_BANG;
		if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
			if (db_last(sp, &lno))
				return (1);
			if (lno == 0) {
				cmdp->addr1.lno = cmdp->addr2.lno = 0;
				ftype = FILTER_RBANG;
			}
		}
		rval = ex_filter(sp, cmdp,
		    &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype);

		/*
		 * If in vi mode, move to the first nonblank.
		 *
		 * !!!
		 * Historic vi wasn't consistent in this area -- if you used
		 * a forward motion it moved to the first nonblank, but if you
		 * did a backward motion it didn't.  And, if you followed a
		 * backward motion with a forward motion, it wouldn't move to
		 * the nonblank for either.  Going to the nonblank generally
		 * seems more useful and consistent, so we do it.
		 */
		sp->lno = rm.lno;
		if (F_ISSET(sp, SC_VI)) {
			sp->cno = 0;
			(void)nonblank(sp, sp->lno, &sp->cno);
		} else
			sp->cno = rm.cno;
	}

	/* Ex terminates with a bang, even if the command fails. */
	if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
		(void)ex_puts(sp, "!\n");

	/*
	 * XXX
	 * The ! commands never return an error, so that autoprint always
	 * happens in the ex parser.
	 */
	return (0);
}
コード例 #22
0
ファイル: v_z.c プロジェクト: 2asoft/freebsd
/*
 * v_z -- [count]z[count][-.+^<CR>]
 *	Move the screen.
 *
 * PUBLIC: int v_z(SCR *, VICMD *);
 */
int
v_z(SCR *sp, VICMD *vp)
{
	recno_t lno;
	e_key_t value;

	/*
	 * The first count is the line to use.  If the value doesn't
	 * exist, use the last line.
	 */
	if (F_ISSET(vp, VC_C1SET)) {
		lno = vp->count;
		if (!db_exist(sp, lno) && db_last(sp, &lno))
			return (1);
	} else
		lno = vp->m_start.lno;

	/* Set default return cursor line. */
	vp->m_final.lno = lno;
	vp->m_final.cno = vp->m_start.cno;

	/*
	 * The second count is the displayed window size, i.e. the 'z' command
	 * is another way to get artificially small windows.  Note, you can't
	 * grow beyond the size of the window.
	 *
	 * !!!
	 * A window size of 0 was historically allowed, and simply ignored.
	 * This could be much more simply done by modifying the value of the
	 * O_WINDOW option, but that's not how it worked historically.
	 */
	if (F_ISSET(vp, VC_C2SET) && vp->count2 != 0) {
		if (vp->count2 > O_VAL(sp, O_WINDOW))
			vp->count2 = O_VAL(sp, O_WINDOW);
		if (vs_crel(sp, vp->count2))
			return (1);
	}

	switch (vp->character) {
	case '-':		/* Put the line at the bottom. */
		if (vs_sm_fill(sp, lno, P_BOTTOM))
			return (1);
		break;
	case '.':		/* Put the line in the middle. */
		if (vs_sm_fill(sp, lno, P_MIDDLE))
			return (1);
		break;
	case '+':
		/*
		 * If the user specified a line number, put that line at the
		 * top and move the cursor to it.  Otherwise, scroll forward
		 * a screen from the current screen.
		 */
		if (F_ISSET(vp, VC_C1SET)) {
			if (vs_sm_fill(sp, lno, P_TOP))
				return (1);
			if (vs_sm_position(sp, &vp->m_final, 0, P_TOP))
				return (1);
		} else
			if (vs_sm_scroll(sp, &vp->m_final, sp->t_rows, Z_PLUS))
				return (1);
		break;
	case '^':
		/*
		 * If the user specified a line number, put that line at the
		 * bottom, move the cursor to it, and then display the screen
		 * before that one.  Otherwise, scroll backward a screen from
		 * the current screen.
		 *
		 * !!!
		 * Note, we match the off-by-one characteristics of historic
		 * vi, here.
		 */
		if (F_ISSET(vp, VC_C1SET)) {
			if (vs_sm_fill(sp, lno, P_BOTTOM))
				return (1);
			if (vs_sm_position(sp, &vp->m_final, 0, P_TOP))
				return (1);
			if (vs_sm_fill(sp, vp->m_final.lno, P_BOTTOM))
				return (1);
		} else
			if (vs_sm_scroll(sp, &vp->m_final, sp->t_rows, Z_CARAT))
				return (1);
		break;
	default:		/* Put the line at the top for <cr>. */
		value = KEY_VAL(sp, vp->character);
		if (value != K_CR && value != K_NL) {
			v_emsg(sp, vp->kp->usage, VIM_USAGE);
			return (1);
		}
		if (vs_sm_fill(sp, lno, P_TOP))
			return (1);
		break;
	}
	return (0);
}
コード例 #23
0
ファイル: ex_script.c プロジェクト: darksoul42/bitrig
/*
 * sscr_insert --
 *	Take a line from the shell and insert it into the file.
 */
static int
sscr_insert(SCR *sp)
{
	CHAR_T *endp, *p, *t;
	SCRIPT *sc;
	struct pollfd pfd[1];
	recno_t lno;
	size_t blen, len, tlen;
	u_int value;
	int nr, rval;
	char *bp;

	/* Find out where the end of the file is. */
	if (db_last(sp, &lno))
		return (1);

#define	MINREAD	1024
	GET_SPACE_RET(sp, bp, blen, MINREAD);
	endp = bp;

	/* Read the characters. */
	rval = 1;
	sc = sp->script;
more:	switch (nr = read(sc->sh_master, endp, MINREAD)) {
	case  0:			/* EOF; shell just exited. */
		sscr_end(sp);
		rval = 0;
		goto ret;
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		goto ret;
	default:
		endp += nr;
		break;
	}

	/* Append the lines into the file. */
	for (p = t = bp; p < endp; ++p) {
		value = KEY_VAL(sp, *p);
		if (value == K_CR || value == K_NL) {
			len = p - t;
			if (db_append(sp, 1, lno++, t, len))
				goto ret;
			t = p + 1;
		}
	}
	if (p > t) {
		len = p - t;
		/*
		 * If the last thing from the shell isn't another prompt, wait
		 * up to 1/10 of a second for more stuff to show up, so that
		 * we don't break the output into two separate lines.  Don't
		 * want to hang indefinitely because some program is hanging,
		 * confused the shell, or whatever.
		 */
		if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
			pfd[0].fd = sc->sh_master;
			pfd[0].events = POLLIN;
			if (poll(pfd, 1, 100) > 0) {
				memmove(bp, t, len);
				endp = bp + len;
				goto more;
			}
		}
		if (sscr_setprompt(sp, t, len))
			return (1);
		if (db_append(sp, 1, lno++, t, len))
			goto ret;
	}

	/* The cursor moves to EOF. */
	sp->lno = lno;
	sp->cno = len ? len - 1 : 0;
	rval = vs_refresh(sp, 1);

ret:	FREE_SPACE(sp, bp, blen);
	return (rval);
}
コード例 #24
0
ファイル: ex_z.c プロジェクト: coyizumi/cs111
/*
 * ex_z -- :[line] z [^-.+=] [count] [flags]
 *	Adjust window.
 *
 * PUBLIC: int ex_z __P((SCR *, EXCMD *));
 */
int
ex_z(SCR *sp, EXCMD *cmdp)
{
    MARK abs;
    recno_t cnt, equals, lno;
    int eofcheck;

    NEEDFILE(sp, cmdp);

    /*
     * !!!
     * If no count specified, use either two times the size of the
     * scrolling region, or the size of the window option.  POSIX
     * 1003.2 claims that the latter is correct, but historic ex/vi
     * documentation and practice appear to use the scrolling region.
     * I'm using the window size as it means that the entire screen
     * is used instead of losing a line to roundoff.  Note, we drop
     * a line from the cnt if using the window size to leave room for
     * the next ex prompt.
     */
    if (FL_ISSET(cmdp->iflags, E_C_COUNT))
        cnt = cmdp->count;
    else
#ifdef HISTORICAL_PRACTICE
        cnt = O_VAL(sp, O_SCROLL) * 2;
#else
        cnt = O_VAL(sp, O_WINDOW) - 1;
#endif

    equals = 0;
    eofcheck = 0;
    lno = cmdp->addr1.lno;

    switch (FL_ISSET(cmdp->iflags,
                     E_C_CARAT | E_C_DASH | E_C_DOT | E_C_EQUAL | E_C_PLUS)) {
    case E_C_CARAT:		/* Display cnt * 2 before the line. */
        eofcheck = 1;
        if (lno > cnt * 2)
            cmdp->addr1.lno = (lno - cnt * 2) + 1;
        else
            cmdp->addr1.lno = 1;
        cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1;
        break;
    case E_C_DASH:		/* Line goes at the bottom of the screen. */
        cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1;
        cmdp->addr2.lno = lno;
        break;
    case E_C_DOT:		/* Line goes in the middle of the screen. */
        /*
         * !!!
         * Historically, the "middleness" of the line overrode the
         * count, so that "3z.19" or "3z.20" would display the first
         * 12 lines of the file, i.e. (N - 1) / 2 lines before and
         * after the specified line.
         */
        eofcheck = 1;
        cnt = (cnt - 1) / 2;
        cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
        cmdp->addr2.lno = lno + cnt;

        /*
         * !!!
         * Historically, z. set the absolute cursor mark.
         */
        abs.lno = sp->lno;
        abs.cno = sp->cno;
        (void)mark_set(sp, ABSMARK1, &abs, 1);
        break;
    case E_C_EQUAL:		/* Center with hyphens. */
        /*
         * !!!
         * Strangeness.  The '=' flag is like the '.' flag (see the
         * above comment, it applies here as well) but with a special
         * little hack.  Print out lines of hyphens before and after
         * the specified line.  Additionally, the cursor remains set
         * on that line.
         */
        eofcheck = 1;
        cnt = (cnt - 1) / 2;
        cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
        cmdp->addr2.lno = lno - 1;
        if (ex_pr(sp, cmdp))
            return (1);
        (void)ex_puts(sp, "----------------------------------------\n");
        cmdp->addr2.lno = cmdp->addr1.lno = equals = lno;
        if (ex_pr(sp, cmdp))
            return (1);
        (void)ex_puts(sp, "----------------------------------------\n");
        cmdp->addr1.lno = lno + 1;
        cmdp->addr2.lno = (lno + cnt) - 1;
        break;
    default:
        /* If no line specified, move to the next one. */
        if (F_ISSET(cmdp, E_ADDR_DEF))
            ++lno;
    /* FALLTHROUGH */
    case E_C_PLUS:		/* Line goes at the top of the screen. */
        eofcheck = 1;
        cmdp->addr1.lno = lno;
        cmdp->addr2.lno = (lno + cnt) - 1;
        break;
    }

    if (eofcheck) {
        if (db_last(sp, &lno))
            return (1);
        if (cmdp->addr2.lno > lno)
            cmdp->addr2.lno = lno;
    }

    if (ex_pr(sp, cmdp))
        return (1);
    if (equals)
        sp->lno = equals;
    return (0);
}
コード例 #25
0
ファイル: search.c プロジェクト: Alkzndr/freebsd
/*
 * b_search --
 *	Do a backward search.
 *
 * PUBLIC: int b_search __P((SCR *,
 * PUBLIC:    MARK *, MARK *, CHAR_T *, size_t, CHAR_T **, u_int));
 */
int
b_search(
	SCR *sp,
	MARK *fm,
	MARK *rm,
	CHAR_T *ptrn,
	size_t plen,
	CHAR_T **eptrn,
	u_int flags)
{
	busy_t btype;
	recno_t lno;
	regmatch_t match[1];
	size_t coff, last, len;
	int cnt, eval, rval, wrapped;
	CHAR_T *l;

	if (search_init(sp, BACKWARD, ptrn, plen, eptrn, flags))
		return (1);

	/*
	 * If doing incremental search, set the "starting" position past the
	 * current column, so that we search a minimal distance and still
	 * match special patterns, e.g., \> for the end of a word.  This is
	 * safe when the cursor is at the end of a line because we only use
	 * it for comparison with the location of the match.
	 *
	 * Otherwise, start searching immediately before the cursor.  If in
	 * the first column, start search on the previous line.
	 */
	if (LF_ISSET(SEARCH_INCR)) {
		lno = fm->lno;
		coff = fm->cno + 1;
	} else {
		if (fm->cno == 0) {
			if (fm->lno == 1 && !O_ISSET(sp, O_WRAPSCAN)) {
				if (LF_ISSET(SEARCH_MSG))
					search_msg(sp, S_SOF);
				return (1);
			}
			lno = fm->lno - 1;
		} else
			lno = fm->lno;
		coff = fm->cno;
	}

	btype = BUSY_ON;
	for (cnt = INTERRUPT_CHECK, rval = 1, wrapped = 0;; --lno, coff = 0) {
		if (cnt-- == 0) {
			if (INTERRUPTED(sp))
				break;
			if (LF_ISSET(SEARCH_MSG)) {
				search_busy(sp, btype);
				btype = BUSY_UPDATE;
			}
			cnt = INTERRUPT_CHECK;
		}
		if ((wrapped && lno < fm->lno) || lno == 0) {
			if (wrapped) {
				if (LF_ISSET(SEARCH_MSG))
					search_msg(sp, S_NOTFOUND);
				break;
			}
			if (!O_ISSET(sp, O_WRAPSCAN)) {
				if (LF_ISSET(SEARCH_MSG))
					search_msg(sp, S_SOF);
				break;
			}
			if (db_last(sp, &lno))
				break;
			if (lno == 0) {
				if (LF_ISSET(SEARCH_MSG))
					search_msg(sp, S_EMPTY);
				break;
			}
			++lno;
			wrapped = 1;
			continue;
		}

		if (db_get(sp, lno, 0, &l, &len))
			break;

		/* Set the termination. */
		match[0].rm_so = 0;
		match[0].rm_eo = len;

#if defined(DEBUG) && 0
		TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
#endif
		/* Search the line. */
		eval = regexec(&sp->re_c, l, 1, match,
		    (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
		if (eval == REG_NOMATCH)
			continue;
		if (eval != 0) {
			if (LF_ISSET(SEARCH_MSG))
				re_error(sp, eval, &sp->re_c);
			else
				(void)sp->gp->scr_bell(sp);
			break;
		}

		/* Check for a match starting past the cursor. */
		if (coff != 0 && match[0].rm_so >= coff)
			continue;

		/* Warn if the search wrapped. */
		if (wrapped && LF_ISSET(SEARCH_WMSG))
			search_msg(sp, S_WRAP);

#if defined(DEBUG) && 0
		TRACE(sp, "B found: %qu to %qu\n",
		    match[0].rm_so, match[0].rm_eo);
#endif
		/*
		 * We now have the first match on the line.  Step through the
		 * line character by character until find the last acceptable
		 * match.  This is painful, we need a better interface to regex
		 * to make this work.
		 */
		for (;;) {
			last = match[0].rm_so++;
			if (match[0].rm_so >= len)
				break;
			match[0].rm_eo = len;
			eval = regexec(&sp->re_c, l, 1, match,
			    (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
			    REG_STARTEND);
			if (eval == REG_NOMATCH)
				break;
			if (eval != 0) {
				if (LF_ISSET(SEARCH_MSG))
					re_error(sp, eval, &sp->re_c);
				else
					(void)sp->gp->scr_bell(sp);
				goto err;
			}
			if (coff && match[0].rm_so >= coff)
				break;
		}
		rm->lno = lno;

		/* See comment in f_search(). */
		if (!LF_ISSET(SEARCH_EOL) && last >= len)
			rm->cno = len != 0 ? len - 1 : 0;
		else
			rm->cno = last;
		rval = 0;
		break;
	}

err:	if (LF_ISSET(SEARCH_MSG))
		search_busy(sp, BUSY_OFF);
	return (rval);
}
コード例 #26
0
ファイル: msg.c プロジェクト: 2asoft/freebsd
/*
 * msgq_status --
 *	Report on the file's status.
 *
 * PUBLIC: void msgq_status(SCR *, recno_t, u_int);
 */
void
msgq_status(
	SCR *sp,
	recno_t lno,
	u_int flags)
{
	recno_t last;
	size_t blen, len;
	int cnt, needsep;
	const char *t;
	char **ap, *bp, *np, *p, *s, *ep;
	CHAR_T *wp;
	size_t wlen;

	/* Get sufficient memory. */
	len = strlen(sp->frp->name);
	GET_SPACE_GOTOC(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128);
	p = bp;
	ep = bp + blen;

	/* Convert the filename. */
	CHAR2INT(sp, sp->frp->name, len + 1, wp, wlen);

	/* Copy in the filename. */
	for (; *wp != '\0'; ++wp) {
		len = KEY_LEN(sp, *wp);
		memcpy(p, KEY_NAME(sp, *wp), len);
		p += len;
	}
	np = p;
	*p++ = ':';
	*p++ = ' ';

	/* Copy in the argument count. */
	if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) {
		for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt);
		if (cnt > 1) {
			(void)snprintf(p, ep - p,
			    msg_cat(sp, "317|%d files to edit", NULL), cnt);
			p += strlen(p);
			*p++ = ':';
			*p++ = ' ';
		}
		F_CLR(sp, SC_STATUS_CNT);
	}

	/*
	 * See nvi/exf.c:file_init() for a description of how and when the
	 * read-only bit is set.
	 *
	 * !!!
	 * The historic display for "name changed" was "[Not edited]".
	 */
	needsep = 0;
	if (F_ISSET(sp->frp, FR_NEWFILE)) {
		F_CLR(sp->frp, FR_NEWFILE);
		t = msg_cat(sp, "021|new file", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	} else {
		if (F_ISSET(sp->frp, FR_NAMECHANGE)) {
			t = msg_cat(sp, "022|name changed", &len);
			memcpy(p, t, len);
			p += len;
			needsep = 1;
		}
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		if (F_ISSET(sp->ep, F_MODIFIED))
			t = msg_cat(sp, "023|modified", &len);
		else
			t = msg_cat(sp, "024|unmodified", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	}
	if (F_ISSET(sp->frp, FR_UNLOCKED)) {
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		t = msg_cat(sp, "025|UNLOCKED", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	}
	if (O_ISSET(sp, O_READONLY)) {
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		t = msg_cat(sp, "026|readonly", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	}
	if (needsep) {
		*p++ = ':';
		*p++ = ' ';
	}
	if (LF_ISSET(MSTAT_SHOWLAST)) {
		if (db_last(sp, &last))
			return;
		if (last == 0) {
			t = msg_cat(sp, "028|empty file", &len);
			memcpy(p, t, len);
			p += len;
		} else {
			t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len);
			(void)snprintf(p, ep - p, t, (u_long)lno, (u_long)last,
			    ((u_long)lno * 100) / last);
			p += strlen(p);
		}
	} else {
		t = msg_cat(sp, "029|line %lu", &len);
		(void)snprintf(p, ep - p, t, (u_long)lno);
		p += strlen(p);
	}
#ifdef DEBUG
	(void)snprintf(p, ep - p, " (pid %lu)", (u_long)getpid());
	p += strlen(p);
#endif
	*p++ = '\n';
	len = p - bp;

	/*
	 * There's a nasty problem with long path names.  Cscope and tags files
	 * can result in long paths and vi will request a continuation key from
	 * the user as soon as it starts the screen.  Unfortunately, the user
	 * has already typed ahead, and chaos results.  If we assume that the
	 * characters in the filenames and informational messages only take a
	 * single screen column each, we can trim the filename.
	 *
	 * XXX
	 * Status lines get put up at fairly awkward times.  For example, when
	 * you do a filter read (e.g., :read ! echo foo) in the top screen of a
	 * split screen, we have to repaint the status lines for all the screens
	 * below the top screen.  We don't want users having to enter continue
	 * characters for those screens.  Make it really hard to screw this up.
	 */
	s = bp;
	if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) {
		for (; s < np && (*s != '/' || (p - s) > sp->cols - 3); ++s);
		if (s == np) {
			s = p - (sp->cols - 5);
			*--s = ' ';
		}
		*--s = '.';
		*--s = '.';
		*--s = '.';
		len = p - s;
	}

	/* Flush any waiting ex messages. */
	(void)ex_fflush(sp);

	sp->gp->scr_msg(sp, M_INFO, s, len);

	FREE_SPACE(sp, bp, blen);
alloc_err:
	return;
}
コード例 #27
0
ファイル: ex_script.c プロジェクト: darksoul42/bitrig
/*
 * sscr_getprompt --
 *	Eat lines printed by the shell until a line with no trailing
 *	carriage return comes; set the prompt from that line.
 */
static int
sscr_getprompt(SCR *sp)
{
	CHAR_T *endp, *p, *t, buf[1024];
	SCRIPT *sc;
	struct pollfd pfd[1];
	recno_t lline;
	size_t llen, len;
	u_int value;
	int nr;

	endp = buf;
	len = sizeof(buf);

	/* Wait up to a second for characters to read. */
	sc = sp->script;
	pfd[0].fd = sc->sh_master;
	pfd[0].events = POLLIN;
	switch (poll(pfd, 1, 5 * 1000)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "poll");
		goto prompterr;
	case  0:		/* Timeout */
		msgq(sp, M_ERR, "Error: timed out");
		goto prompterr;
	default:		/* Characters to read. */
		break;
	}

	/* Read the characters. */
more:	len = sizeof(buf) - (endp - buf);
	switch (nr = read(sc->sh_master, endp, len)) {
	case  0:			/* EOF. */
		msgq(sp, M_ERR, "Error: shell: EOF");
		goto prompterr;
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		goto prompterr;
	default:
		endp += nr;
		break;
	}

	/* If any complete lines, push them into the file. */
	for (p = t = buf; p < endp; ++p) {
		value = KEY_VAL(sp, *p);
		if (value == K_CR || value == K_NL) {
			if (db_last(sp, &lline) ||
			    db_append(sp, 0, lline, t, p - t))
				goto prompterr;
			t = p + 1;
		}
	}
	if (p > buf) {
		memmove(buf, t, endp - t);
		endp = buf + (endp - t);
	}
	if (endp == buf)
		goto more;

	/* Wait up 1/10 of a second to make sure that we got it all. */
	switch (poll(pfd, 1, 100)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "poll");
		goto prompterr;
	case  0:		/* Timeout */
		break;
	default:		/* Characters to read. */
		goto more;
	}

	/* Timed out, so theoretically we have a prompt. */
	llen = endp - buf;
	endp = buf;

	/* Append the line into the file. */
	if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) {
prompterr:	sscr_end(sp);
		return (1);
	}

	return (sscr_setprompt(sp, buf, llen));
}
コード例 #28
0
ファイル: ex_subst.c プロジェクト: mosconi/openbsd
static int
s(SCR *sp, EXCMD *cmdp, char *s, regex_t *re, u_int flags)
{
	EVENT ev;
	MARK from, to;
	TEXTH tiq;
	recno_t elno, lno, slno;
	regmatch_t match[10];
	size_t blen, cnt, last, lbclen, lblen, len, llen;
	size_t offset, saved_offset, scno;
	int lflag, nflag, pflag, rflag;
	int didsub, do_eol_match, eflags, empty_ok, eval;
	int linechanged, matched, quit, rval;
	unsigned long ul;
	char *bp, *lb;

	NEEDFILE(sp, cmdp);

	slno = sp->lno;
	scno = sp->cno;

	/*
	 * !!!
	 * Historically, the 'g' and 'c' suffices were always toggled as flags,
	 * so ":s/A/B/" was the same as ":s/A/B/ccgg".  If O_EDCOMPATIBLE was
	 * not set, they were initialized to 0 for all substitute commands.  If
	 * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user
	 * specified substitute/replacement patterns (see ex_s()).
	 */
	if (!O_ISSET(sp, O_EDCOMPATIBLE))
		sp->c_suffix = sp->g_suffix = 0;

	/*
	 * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but
	 * it only displayed the last change.  I'd disallow them, but they are
	 * useful in combination with the [v]global commands.  In the current
	 * model the problem is combining them with the 'c' flag -- the screen
	 * would have to flip back and forth between the confirm screen and the
	 * ex print screen, which would be pretty awful.  We do display all
	 * changes, though, for what that's worth.
	 *
	 * !!!
	 * Historic vi was fairly strict about the order of "options", the
	 * count, and "flags".  I'm somewhat fuzzy on the difference between
	 * options and flags, anyway, so this is a simpler approach, and we
	 * just take it them in whatever order the user gives them.  (The ex
	 * usage statement doesn't reflect this.)
	 */
	lflag = nflag = pflag = rflag = 0;
	if (s == NULL)
		goto noargs;
	for (lno = OOBLNO; *s != '\0'; ++s)
		switch (*s) {
		case ' ':
		case '\t':
			continue;
		case '+':
			++cmdp->flagoff;
			break;
		case '-':
			--cmdp->flagoff;
			break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			if (lno != OOBLNO)
				goto usage;
			errno = 0;
			if ((ul = strtoul(s, &s, 10)) >= UINT_MAX)
				errno = ERANGE;
			if (*s == '\0')		/* Loop increment correction. */
				--s;
			if (errno == ERANGE) {
				if (ul >= UINT_MAX)
					msgq(sp, M_ERR, "Count overflow");
				else
					msgq(sp, M_SYSERR, NULL);
				return (1);
			}
			lno = (recno_t)ul;
			/*
			 * In historic vi, the count was inclusive from the
			 * second address.
			 */
			cmdp->addr1.lno = cmdp->addr2.lno;
			cmdp->addr2.lno += lno - 1;
			if (!db_exist(sp, cmdp->addr2.lno) &&
			    db_last(sp, &cmdp->addr2.lno))
				return (1);
			break;
		case '#':
			nflag = 1;
			break;
		case 'c':
			sp->c_suffix = !sp->c_suffix;

			/* Ex text structure initialization. */
			if (F_ISSET(sp, SC_EX)) {
				memset(&tiq, 0, sizeof(TEXTH));
				TAILQ_INIT(&tiq);
			}
			break;
		case 'g':
			sp->g_suffix = !sp->g_suffix;
			break;
		case 'l':
			lflag = 1;
			break;
		case 'p':
			pflag = 1;
			break;
		case 'r':
			if (LF_ISSET(SUB_FIRST)) {
				msgq(sp, M_ERR,
		    "Regular expression specified; r flag meaningless");
				return (1);
			}
			if (!F_ISSET(sp, SC_RE_SEARCH)) {
				ex_emsg(sp, NULL, EXM_NOPREVRE);
				return (1);
			}
			rflag = 1;
			re = &sp->re_c;
			break;
		default:
			goto usage;
		}

	if (*s != '\0' || (!rflag && LF_ISSET(SUB_MUSTSETR))) {
usage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
		return (1);
	}

noargs:	if (F_ISSET(sp, SC_VI) && sp->c_suffix && (lflag || nflag || pflag)) {
		msgq(sp, M_ERR,
"The #, l and p flags may not be combined with the c flag in vi mode");
		return (1);
	}

	/*
	 * bp:		if interactive, line cache
	 * blen:	if interactive, line cache length
	 * lb:		build buffer pointer.
	 * lbclen:	current length of built buffer.
	 * lblen;	length of build buffer.
	 */
	bp = lb = NULL;
	blen = lbclen = lblen = 0;

	/* For each line... */
	for (matched = quit = 0, lno = cmdp->addr1.lno,
	    elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) {

		/* Someone's unhappy, time to stop. */
		if (INTERRUPTED(sp))
			break;

		/* Get the line. */
		if (db_get(sp, lno, DBG_FATAL, &s, &llen))
			goto err;

		/*
		 * Make a local copy if doing confirmation -- when calling
		 * the confirm routine we're likely to lose the cached copy.
		 */
		if (sp->c_suffix) {
			if (bp == NULL) {
				GET_SPACE_RET(sp, bp, blen, llen);
			} else
				ADD_SPACE_RET(sp, bp, blen, llen);
			memcpy(bp, s, llen);
			s = bp;
		}

		/* Start searching from the beginning. */
		offset = 0;
		len = llen;

		/* Reset the build buffer offset. */
		lbclen = 0;

		/* Reset empty match flag. */
		empty_ok = 1;

		/*
		 * We don't want to have to do a setline if the line didn't
		 * change -- keep track of whether or not this line changed.
		 * If doing confirmations, don't want to keep setting the
		 * line if change is refused -- keep track of substitutions.
		 */
		didsub = linechanged = 0;

		/* New line, do an EOL match. */
		do_eol_match = 1;

		/* It's not nul terminated, but we pretend it is. */
		eflags = REG_STARTEND;

		/*
		 * The search area is from s + offset to the EOL.
		 *
		 * Generally, match[0].rm_so is the offset of the start
		 * of the match from the start of the search, and offset
		 * is the offset of the start of the last search.
		 */
nextmatch:	match[0].rm_so = 0;
		match[0].rm_eo = len;

		/* Get the next match. */
		eval = regexec(re, (char *)s + offset, 10, match, eflags);

		/*
		 * There wasn't a match or if there was an error, deal with
		 * it.  If there was a previous match in this line, resolve
		 * the changes into the database.  Otherwise, just move on.
		 */
		if (eval == REG_NOMATCH)
			goto endmatch;
		if (eval != 0) {
			re_error(sp, eval, re);
			goto err;
		}
		matched = 1;

		/* Only the first search can match an anchored expression. */
		eflags |= REG_NOTBOL;

		/*
		 * !!!
		 * It's possible to match 0-length strings -- for example, the
		 * command s;a*;X;, when matched against the string "aabb" will
		 * result in "XbXbX", i.e. the matches are "aa", the space
		 * between the b's and the space between the b's and the end of
		 * the string.  There is a similar space between the beginning
		 * of the string and the a's.  The rule that we use (because vi
		 * historically used it) is that any 0-length match, occurring
		 * immediately after a match, is ignored.  Otherwise, the above
		 * example would have resulted in "XXbXbX".  Another example is
		 * incorrectly using " *" to replace groups of spaces with one
		 * space.
		 *
		 * The way we do this is that if we just had a successful match,
		 * the starting offset does not skip characters, and the match
		 * is empty, ignore the match and move forward.  If there's no
		 * more characters in the string, we were attempting to match
		 * after the last character, so quit.
		 */
		if (!empty_ok && match[0].rm_so == 0 && match[0].rm_eo == 0) {
			empty_ok = 1;
			if (len == 0)
				goto endmatch;
			BUILD(sp, s + offset, 1)
			++offset;
			--len;
			goto nextmatch;
		}

		/* Confirm change. */
		if (sp->c_suffix) {
			/*
			 * Set the cursor position for confirmation.  Note,
			 * if we matched on a '$', the cursor may be past
			 * the end of line.
			 */
			from.lno = to.lno = lno;
			from.cno = match[0].rm_so + offset;
			to.cno = match[0].rm_eo + offset;
			/*
			 * Both ex and vi have to correct for a change before
			 * the first character in the line.
			 */
			if (llen == 0)
				from.cno = to.cno = 0;
			if (F_ISSET(sp, SC_VI)) {
				/*
				 * Only vi has to correct for a change after
				 * the last character in the line.
				 *
				 * XXX
				 * It would be nice to change the vi code so
				 * that we could display a cursor past EOL.
				 */
				if (to.cno >= llen)
					to.cno = llen - 1;
				if (from.cno >= llen)
					from.cno = llen - 1;

				sp->lno = from.lno;
				sp->cno = from.cno;
				if (vs_refresh(sp, 1))
					goto err;

				vs_update(sp, "Confirm change? [n]", NULL);

				if (v_event_get(sp, &ev, 0, 0))
					goto err;
				switch (ev.e_event) {
				case E_CHARACTER:
					break;
				case E_EOF:
				case E_ERR:
				case E_INTERRUPT:
					goto lquit;
				default:
					v_event_err(sp, &ev);
					goto lquit;
				}
			} else {
				if (ex_print(sp, cmdp, &from, &to, 0) ||
				    ex_scprint(sp, &from, &to))
					goto lquit;
				if (ex_txt(sp, &tiq, 0, TXT_CR))
					goto err;
				ev.e_c = TAILQ_FIRST(&tiq)->lb[0];
			}

			switch (ev.e_c) {
			case CH_YES:
				break;
			default:
			case CH_NO:
				didsub = 0;
				BUILD(sp, s +offset, match[0].rm_eo);
				goto skip;
			case CH_QUIT:
				/* Set the quit/interrupted flags. */
lquit:				quit = 1;
				F_SET(sp->gp, G_INTERRUPTED);

				/*
				 * Resolve any changes, then return to (and
				 * exit from) the main loop.
				 */
				goto endmatch;
			}
		}

		/*
		 * Set the cursor to the last position changed, converting
		 * from 1-based to 0-based.
		 */
		sp->lno = lno;
		sp->cno = match[0].rm_so;

		/* Copy the bytes before the match into the build buffer. */
		BUILD(sp, s + offset, match[0].rm_so);

		/* Substitute the matching bytes. */
		didsub = 1;
		if (re_sub(sp, s + offset, &lb, &lbclen, &lblen, match))
			goto err;

		/* Set the change flag so we know this line was modified. */
		linechanged = 1;

		/* Move past the matched bytes. */
skip:		offset += match[0].rm_eo;
		len -= match[0].rm_eo;

		/* A match cannot be followed by an empty pattern. */
		empty_ok = 0;

		/*
		 * If doing a global change with confirmation, we have to
		 * update the screen.  The basic idea is to store the line
		 * so the screen update routines can find it, and restart.
		 */
		if (didsub && sp->c_suffix && sp->g_suffix) {
			/*
			 * The new search offset will be the end of the
			 * modified line.
			 */
			saved_offset = lbclen;

			/* Copy the rest of the line. */
			if (len)
				BUILD(sp, s + offset, len)

			/* Set the new offset. */
			offset = saved_offset;

			/* Store inserted lines, adjusting the build buffer. */
			last = 0;
			if (sp->newl_cnt) {
				for (cnt = 0;
				    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
					if (db_insert(sp, lno,
					    lb + last, sp->newl[cnt] - last))
						goto err;
					last = sp->newl[cnt] + 1;
					++sp->rptlines[L_ADDED];
				}
				lbclen -= last;
				offset -= last;
				sp->newl_cnt = 0;
			}

			/* Store and retrieve the line. */
			if (db_set(sp, lno, lb + last, lbclen))
				goto err;
			if (db_get(sp, lno, DBG_FATAL, &s, &llen))
				goto err;
			ADD_SPACE_RET(sp, bp, blen, llen)
			memcpy(bp, s, llen);
			s = bp;
			len = llen - offset;

			/* Restart the build. */
			lbclen = 0;
			BUILD(sp, s, offset);

			/*
			 * If we haven't already done the after-the-string
			 * match, do one.  Set REG_NOTEOL so the '$' pattern
			 * only matches once.
			 */
			if (!do_eol_match)
				goto endmatch;
			if (offset == len) {
				do_eol_match = 0;
				eflags |= REG_NOTEOL;
			}
			goto nextmatch;
		}

		/*
		 * If it's a global:
		 *
		 * If at the end of the string, do a test for the after
		 * the string match.  Set REG_NOTEOL so the '$' pattern
		 * only matches once.
		 */
		if (sp->g_suffix && do_eol_match) {
			if (len == 0) {
				do_eol_match = 0;
				eflags |= REG_NOTEOL;
			}
			goto nextmatch;
		}

endmatch:	if (!linechanged)
			continue;

		/* Copy any remaining bytes into the build buffer. */
		if (len)
			BUILD(sp, s + offset, len)

		/* Store inserted lines, adjusting the build buffer. */
		last = 0;
		if (sp->newl_cnt) {
			for (cnt = 0;
			    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
				if (db_insert(sp,
				    lno, lb + last, sp->newl[cnt] - last))
					goto err;
				last = sp->newl[cnt] + 1;
				++sp->rptlines[L_ADDED];
			}
			lbclen -= last;
			sp->newl_cnt = 0;
		}

		/* Store the changed line. */
		if (db_set(sp, lno, lb + last, lbclen))
			goto err;

		/* Update changed line counter. */
		if (sp->rptlchange != lno) {
			sp->rptlchange = lno;
			++sp->rptlines[L_CHANGED];
		}

		/*
		 * !!!
		 * Display as necessary.  Historic practice is to only
		 * display the last line of a line split into multiple
		 * lines.
		 */
		if (lflag || nflag || pflag) {
			from.lno = to.lno = lno;
			from.cno = to.cno = 0;
			if (lflag)
				(void)ex_print(sp, cmdp, &from, &to, E_C_LIST);
			if (nflag)
				(void)ex_print(sp, cmdp, &from, &to, E_C_HASH);
			if (pflag)
				(void)ex_print(sp, cmdp, &from, &to, E_C_PRINT);
		}
	}

	/*
	 * !!!
	 * Historically, vi attempted to leave the cursor at the same place if
	 * the substitution was done at the current cursor position.  Otherwise
	 * it moved it to the first non-blank of the last line changed.  There
	 * were some problems: for example, :s/$/foo/ with the cursor on the
	 * last character of the line left the cursor on the last character, or
	 * the & command with multiple occurrences of the matching string in the
	 * line usually left the cursor in a fairly random position.
	 *
	 * We try to do the same thing, with the exception that if the user is
	 * doing substitution with confirmation, we move to the last line about
	 * which the user was consulted, as opposed to the last line that they
	 * actually changed.  This prevents a screen flash if the user doesn't
	 * change many of the possible lines.
	 */
	if (!sp->c_suffix && (sp->lno != slno || sp->cno != scno)) {
		sp->cno = 0;
		(void)nonblank(sp, sp->lno, &sp->cno);
	}

	/*
	 * If not in a global command, and nothing matched, say so.
	 * Else, if none of the lines displayed, put something up.
	 */
	rval = 0;
	if (!matched) {
		if (!F_ISSET(sp, SC_EX_GLOBAL)) {
			msgq(sp, M_ERR, "No match found");
			goto err;
		}
	} else if (!lflag && !nflag && !pflag)
		F_SET(cmdp, E_AUTOPRINT);

	if (0) {
err:		rval = 1;
	}

	if (bp != NULL)
		FREE_SPACE(sp, bp, blen);
	if (lb != NULL)
		free(lb);
	return (rval);
}
コード例 #29
0
ファイル: ex_script.c プロジェクト: Hooman3/minix
/*
 * sscr_getprompt --
 *	Eat lines printed by the shell until a line with no trailing
 *	carriage return comes; set the prompt from that line.
 */
static int
sscr_getprompt(SCR *sp)
{
	struct timeval tv;
	CHAR_T *endp, *p, *t, buf[1024];
	SCRIPT *sc;
	fd_set fdset;
	db_recno_t lline;
	size_t llen, len;
	e_key_t value;
	int nr;

	FD_ZERO(&fdset);
	endp = buf;
	len = sizeof(buf);

	/* Wait up to a second for characters to read. */
	tv.tv_sec = 5;
	tv.tv_usec = 0;
	sc = sp->script;
	FD_SET(sc->sh_master, &fdset);
	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "select");
		goto prompterr;
	case  0:		/* Timeout */
		msgq(sp, M_ERR, "Error: timed out");
		goto prompterr;
	case  1:		/* Characters to read. */
		break;
	}

	/* Read the characters. */
more:	len = sizeof(buf) - (endp - buf);
	switch (nr = read(sc->sh_master, endp, len)) {
	case  0:			/* EOF. */
		msgq(sp, M_ERR, "Error: shell: EOF");
		goto prompterr;
	case -1:			/* Error or interrupt. */
		msgq(sp, M_SYSERR, "shell");
		goto prompterr;
	default:
		endp += nr;
		break;
	}

	/* If any complete lines, push them into the file. */
	for (p = t = buf; p < endp; ++p) {
		value = KEY_VAL(sp, *p);
		if (value == K_CR || value == K_NL) {
			if (db_last(sp, &lline) ||
			    db_append(sp, 0, lline, t, p - t))
				goto prompterr;
			t = p + 1;
		}
	}
	if (p > buf) {
		MEMMOVE(buf, t, endp - t);
		endp = buf + (endp - t);
	}
	if (endp == buf)
		goto more;

	/* Wait up 1/10 of a second to make sure that we got it all. */
	tv.tv_sec = 0;
	tv.tv_usec = 100000;
	switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
	case -1:		/* Error or interrupt. */
		msgq(sp, M_SYSERR, "select");
		goto prompterr;
	case  0:		/* Timeout */
		break;
	case  1:		/* Characters to read. */
		goto more;
	}

	/* Timed out, so theoretically we have a prompt. */
	llen = endp - buf;
	endp = buf;

	/* Append the line into the file. */
	if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) {
prompterr:	sscr_end(sp);
		return (1);
	}

	return (sscr_setprompt(sp, buf, llen));
}