⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 svi_ex.c

📁 早期freebsd实现
💻 C
字号:
/*- * Copyright (c) 1993, 1994 *	The Regents of the University of California.  All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)svi_ex.c	8.45 (Berkeley) 4/14/94";#endif /* not lint */#include <sys/types.h>#include <sys/queue.h>#include <sys/time.h>#include <bitstring.h>#include <ctype.h>#include <errno.h>#include <limits.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <unistd.h>#include "compat.h"#include <curses.h>#include <db.h>#include <regex.h>#include "vi.h"#include "vcmd.h"#include "excmd.h"#include "svi_screen.h"#include "../sex/sex_screen.h"static int	svi_ex_done __P((SCR *, EXF *, MARK *));static int	svi_ex_scroll __P((SCR *, int, int, CH *));/* * svi_ex_cmd -- *	Execute an ex command. */intsvi_ex_cmd(sp, ep, exp, rp)	SCR *sp;	EXF *ep;	EXCMDARG *exp;	MARK *rp;{	SVI_PRIVATE *svp;	int rval;	svp = SVP(sp);	svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;	(void)svi_busy(sp, NULL);	rval = exp->cmd->fn(sp, ep, exp);	(void)msg_rpt(sp, 0);	(void)ex_fflush(EXCOOKIE);	/*	 * If displayed anything, figure out if we have to wait.  If the	 * screen wasn't trashed, only one line and there are no waiting	 * messages, don't wait, but don't overwrite it with mode information	 * either.  If there's a screen under this one, change the line to	 * inverse video.	 */	if (svp->extotalcount > 0)		if (!F_ISSET(sp, S_REFRESH) && svp->extotalcount == 1 &&		    (sp->msgq.lh_first == NULL ||		    F_ISSET(sp->msgq.lh_first, M_EMPTY)))			F_SET(sp, S_UPDATE_MODE);		else {			/* This message isn't interruptible. */			F_CLR(sp, S_INTERRUPTIBLE);			(void)svi_ex_scroll(sp, 1, 0, NULL);		}	return (svi_ex_done(sp, ep, rp) || rval);}/* * svi_ex_run -- *	Execute strings of ex commands. */intsvi_ex_run(sp, ep, rp)	SCR *sp;	EXF *ep;	MARK *rp;{	enum input (*get) __P((SCR *, EXF *, TEXTH *, ARG_CHAR_T, u_int));	struct termios t;	CH ikey;	SVI_PRIVATE *svp;	TEXT *tp;	int flags, in_exmode, rval;	svp = SVP(sp);	svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;	/*	 * There's some tricky stuff going on here to handle when a user has	 * mapped a key to multiple ex commands.  Historic practice was that	 * vi ran without any special actions, as if the user were entering	 * the characters, until ex trashed the screen, e.g. something like a	 * '!' command.  At that point, we no longer know what the screen	 * looks like, so we can't afford to overwrite anything.  The solution	 * is to go into real ex mode until we get to the end of the command	 * strings.	 */	get = svi_get;	flags = TXT_BS | TXT_PROMPT;	for (in_exmode = rval = 0;;) {		/*		 * Get the next command.  Interrupt flag manipulation is safe		 * because ex_icmd clears them all.		 */		F_SET(sp, S_INTERRUPTIBLE);		if (get(sp, ep, sp->tiqp, ':', flags) != INP_OK) {			rval = 1;			break;		}		if (F_ISSET(sp, S_INTERRUPTED))			break;		/*		 * Len is 0 if the user backspaced over the prompt,		 * 1 if only a CR was entered.		 */		tp = sp->tiqp->cqh_first;		if (tp->len == 0)			break;		if (!in_exmode)			(void)svi_busy(sp, NULL);		/* Ignore return, presumably an error message was displayed. */		(void)ex_icmd(sp, ep, tp->lb, tp->len);		(void)ex_fflush(EXCOOKIE);		/*		 * The file or screen may have changed, in which case, the		 * main editor loop takes care of it.		 */		if (F_ISSET(sp, S_MAJOR_CHANGE))			break;		/*		 * If continue not required, and one or no lines, and there		 * are no waiting messages, don't wait, but don't overwrite		 * it with mode information either.  If there's a screen under		 * this one, change the line to inverse video.		 */		if (!F_ISSET(sp, S_CONTINUE) &&		    (svp->extotalcount == 0 || svp->extotalcount == 1 &&		    (sp->msgq.lh_first == NULL ||		    F_ISSET(sp->msgq.lh_first, M_EMPTY)))) {			if (svp->extotalcount == 1)				F_SET(sp, S_UPDATE_MODE);			break;		}		/* If the screen is trashed, go into ex mode. */		if (!in_exmode && F_ISSET(sp, S_REFRESH)) {			/* Initialize the terminal state. */			if (F_ISSET(sp->gp, G_STDIN_TTY))				SEX_RAW(t);			get = sex_get;			flags = TXT_CR | TXT_NLECHO | TXT_PROMPT;			in_exmode = 1;		}		/*		 * If the user hasn't already indicated that they're done,		 * they may continue in ex mode by entering a ':'.		 */		if (F_ISSET(sp, S_INTERRUPTED))			break;		if (in_exmode) {			(void)write(STDOUT_FILENO,			    STR_CMSG, sizeof(STR_CMSG) - 1);			for (;;) {				if (term_user_key(sp, &ikey) != INP_OK) {					rval = 1;					goto ret;				}				if (ikey.ch == ' ' || ikey.ch == ':')					break;				if (ikey.value == K_CR || ikey.value == K_NL)					break;				sex_bell(sp);			}		} else {			/* This message isn't interruptible. */			F_CLR(sp, S_INTERRUPTIBLE);			(void)svi_ex_scroll(sp, 1, 1, &ikey);		}		if (ikey.ch != ':')                        break;		if (in_exmode)			(void)write(STDOUT_FILENO, "\r\n", 2);		else {			++svp->extotalcount;			++svp->exlinecount;		}	}ret:	if (in_exmode) {		/* Reset the terminal state. */		if (F_ISSET(sp->gp, G_STDIN_TTY) && SEX_NORAW(t))			rval = 1;		F_SET(sp, S_REFRESH);	} else		if (svi_ex_done(sp, ep, rp))			rval = 1;	F_CLR(sp, S_CONTINUE);	return (rval);}/* * svi_ex_done -- *	Cleanup from dipping into ex. */static intsvi_ex_done(sp, ep, rp)	SCR *sp;	EXF *ep;	MARK *rp;{	SMAP *smp;	SVI_PRIVATE *svp;	recno_t lno;	size_t cnt, len;	/*	 * The file or screen may have changed, in which case,	 * the main editor loop takes care of it.	 */	if (F_ISSET(sp, S_MAJOR_CHANGE))		return (0);	/*	 * Otherwise, the only cursor modifications will be real, however, the	 * underlying line may have changed; don't trust anything.  This code	 * has been a remarkably fertile place for bugs.	 *	 * Repaint the entire screen if at least half the screen is trashed.	 * Else, repaint only over the overwritten lines.  The "-2" comes	 * from one for the mode line and one for the fact that it's an offset.	 * Note the check for small screens.	 *	 * Don't trust ANYTHING.	 */	svp = SVP(sp);	if (svp->extotalcount >= HALFTEXT(sp))		F_SET(sp, S_REDRAW);	else		for (cnt = sp->rows - 2; svp->extotalcount--; --cnt)			if (cnt > sp->t_rows) {				MOVE(sp, cnt, 0);				clrtoeol();			} else {				smp = HMAP + cnt;				SMAP_FLUSH(smp);				if (svi_line(sp, ep, smp, NULL, NULL))					return (1);			}	/*	 * Do a reality check on a cursor value, and make sure it's okay.	 * If necessary, change it.  Ex keeps track of the line number,	 * but ex doesn't care about the column and it may have disappeared.	 */	if (file_gline(sp, ep, sp->lno, &len) == NULL) {		if (file_lline(sp, ep, &lno))			return (1);		if (lno != 0)			GETLINE_ERR(sp, sp->lno);		sp->lno = 1;		sp->cno = 0;	} else if (sp->cno >= len)		sp->cno = len ? len - 1 : 0;	rp->lno = sp->lno;	rp->cno = sp->cno;	return (0);}/* * svi_ex_write -- *	Write out the ex messages. * * There is no tab or character translation going on, so, whatever the ex * and/or curses routines do with special characters is all that gets done. * This is probably okay, I don't see any reason that user's tab settings * should affect ex output, and ex should have displayed everything else * exactly as it wanted it on the screen. */intsvi_ex_write(cookie, line, llen)	void *cookie;	const char *line;	int llen;{	SCR *sp;	SVI_PRIVATE *svp;	size_t oldy, oldx;	int len, rlen;	const char *p;	/*	 * XXX	 * If it's a 4.4BSD system, we could just use fpurge(3).	 * This shouldn't be too expensive, though.	 */	sp = cookie;	svp = SVP(sp);	if (F_ISSET(sp, S_INTERRUPTED))		return (llen);	p = line;			/* In case of a write of 0. */	for (rlen = llen; llen;) {		/* Get the next line. */		if ((p = memchr(line, '\n', llen)) == NULL)			len = llen;		else			len = p - line;		/*		 * The max is sp->cols characters, and we may		 * have already written part of the line.		 */		if (len + svp->exlcontinue > sp->cols)			len = sp->cols - svp->exlcontinue;		/*		 * If the first line output, do nothing.		 * If the second line output, draw the divider line.		 * If drew a full screen, remove the divider line.		 * If it's a continuation line, move to the continuation		 * point, else, move the screen up.		 */		if (svp->exlcontinue == 0) {			if (svp->extotalcount == 1) {				MOVE(sp, INFOLINE(sp) - 1, 0);				clrtoeol();				if (svi_divider(sp))					return (-1);				F_SET(svp, SVI_DIVIDER);				++svp->extotalcount;				++svp->exlinecount;			}			if (svp->extotalcount == sp->t_maxrows &&			    F_ISSET(svp, SVI_DIVIDER)) {				--svp->extotalcount;				--svp->exlinecount;				F_CLR(svp, SVI_DIVIDER);			}			if (svp->extotalcount != 0 &&			    svi_ex_scroll(sp, 0, 0, NULL))				return (-1);			MOVE(sp, INFOLINE(sp), 0);			++svp->extotalcount;			++svp->exlinecount;			if (F_ISSET(sp, S_INTERRUPTIBLE) &&			    F_ISSET(sp, S_INTERRUPTED))				break;		} else			MOVE(sp, INFOLINE(sp), svp->exlcontinue);		/* Display the line. */		if (len)			ADDNSTR(line, len);		/* Clear to EOL. */		getyx(stdscr, oldy, oldx);		if (oldx < sp->cols)			clrtoeol();		/* If we loop, it's a new line. */		svp->exlcontinue = 0;		/* Reset for the next line. */		line += len;		llen -= len;		if (p != NULL) {			++line;			--llen;		}	}	/* Refresh the screen, even if it's a partial. */	refresh();	/* Set up next continuation line. */	if (p == NULL)		getyx(stdscr, oldy, svp->exlcontinue);	return (rlen);}/* * svi_ex_scroll -- *	Scroll the screen for ex output. */static intsvi_ex_scroll(sp, mustwait, colon_ok, chp)	SCR *sp;	int mustwait, colon_ok;	CH *chp;{	CH ikey;	SVI_PRIVATE *svp;	/*	 * Scroll the screen.  Instead of scrolling the entire screen, delete	 * the line above the first line output so preserve the maximum amount	 * of the screen.	 */	svp = SVP(sp);	if (svp->extotalcount >= sp->rows) {		MOVE(sp, 0, 0);	} else		MOVE(sp, INFOLINE(sp) - svp->extotalcount, 0);	deleteln();	/* If there are screens below us, push them back into place. */	if (sp->q.cqe_next != (void *)&sp->gp->dq) {		MOVE(sp, INFOLINE(sp), 0);		insertln();	}	/* If just displayed a full screen, wait. */	if (mustwait || svp->exlinecount == sp->t_maxrows) {		MOVE(sp, INFOLINE(sp), 0);		if (F_ISSET(sp, S_INTERRUPTIBLE)) {			ADDNSTR(STR_QMSG, (int)sizeof(STR_QMSG) - 1);		} else {			ADDNSTR(STR_CMSG, (int)sizeof(STR_CMSG) - 1);		}		clrtoeol();		refresh();		for (;;) {			if (term_user_key(sp, &ikey) != INP_OK)				return (-1);			if (ikey.ch == ' ')				break;			if (colon_ok && ikey.ch == ':')				break;			if (ikey.value == K_CR || ikey.value == K_NL)				break;			if (ikey.ch == CH_QUIT &&			    F_ISSET(sp, S_INTERRUPTIBLE)) {				F_SET(sp, S_INTERRUPTED);				break;			}			svi_bell(sp);		}		if (chp != NULL)			*chp = ikey;		svp->exlinecount = 0;	}	return (0);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -