sh.edit.c

来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 3,065 行 · 第 1/4 页

C
3,065
字号
#ifdef CSHEDIT#ifndef lintstatic	char	*sccsid = "@(#)sh.edit.c	4.2	(ULTRIX)	12/20/90";#endif lint/************************************************************************ *									* *			Copyright (c) 1986 by				* *		Digital Equipment Corporation, Maynard, MA		* *			All rights reserved.				* *									* *   This software is furnished under a license and may be used and	* *   copied  only  in accordance with the terms of such license and	* *   with the  inclusion  of  the  above  copyright  notice.   This	* *   software  or  any  other copies thereof may not be provided or	* *   otherwise made available to any other person.  No title to and	* *   ownership of the software is hereby transferred.			* *									* *   This software is  derived  from  software  received  from  the	* *   University    of   California,   Berkeley,   and   from   Bell	* *   Laboratories.  Use, duplication, or disclosure is  subject  to	* *   restrictions  under  license  agreements  with  University  of	* *   California and with AT&T.						* *									* *   The information in this software is subject to change  without	* *   notice  and should not be construed as a commitment by Digital	* *   Equipment Corporation.						* *									* *   Digital assumes no responsibility for the use  or  reliability	* *   of its software on equipment which is not supplied by Digital.	* *									* ************************************************************************//* * Modification History:	sh.edit.c * * Edit routines to edit a command line buffer.  The lower level routines * are adapted from ex/vi code.  The Top level command processors for both * vi style and emacs style editing are original. * * 009 - Bob Fontaine - Mon Oct 22 10:51:40 EDT 1990 *      Prevent csh from hanging when cntrl-\ is typed in emacs editing mode. *      Thanks to Akira Tanaka (JRD).  Fixes QAR 5522. * * 008 - Bob Fontaine - Mon Oct 22 10:51:40 EDT 1990 *      Fix expansion of wild card characters after command line edit mode *      has been entered.  Thanks to Akri Tanaka (JRD).  Fixes qar 5264. * * 007 - Gary A. Gaudet - Tue Feb 27 10:17:01 EST 1990 *	Fixed <esc>" infinite loop problem * * 006 - Gary A. Gaudet - Thu Dec 28 17:49:45 EST 1989 *	Removed unused variables: oglobp, holdcurs. arg, ret, i, col, *		cnt, escape, holdcurs, x, d, colp, l, c *	Modified return values. *	Removed unused label: egetcount, bakchar, again *	Added test for ioctl() return value. * * 005 - 26-Sep-88	afd *	Added ^P/^N to scroll up/down thru the history list. * * 004 - 22-Dec-87	afd *	Made the edit code "8 bit clean".  We never set nor clear the 8th bit. * * 003 - 23-Jul-87	afd *	Added history search capability: to vmain() and emain() *	command parsers and added srchback() routine to get search string. *	Made redraw into a function which is called in several places. *	Added "insearch" flag to keep track of when we are in emacs *	"search mode" to repeat history searches. * * 002 - 15-Jul-87	afd *	Fixed repeat count bug on 'r', "insert", "append" and "put" commands by  *		NOT using "printf" in vappend().  printf is the shell's version *		and it prints to a buffer which is flushed later. *	Set DEL and INS buffers to 0 in init_globals before editing a cmd line. *	Set lastcmd buffer to 0 in init_globals before editing a cmd line. * * 001 - 28-Nov-86	afd *	Created this file for command line edit capability in csh. */#include "sh.edit.h"#define SRCHLEN 78		/* also in sh.editglue.c */int vdebug = 0;#define printd if (vdebug) printfextern short SHIN, SHOUT, SHDIAG;	 /* csh I/O file descriptors */extern char srchcmd[];		/* cmd to search for in history list *//* * The routines outchar and putchar and are actually variables, * and these variables point at the current definitions of the routines. * Outchar and Putchar will be set to vputchar, vinschar, etc. */int	(*Outchar)() = vputchar;int	(*Putchar)() = normchar;/* * Main edit procedure. * Transfers control to the main command processing loop, vmain(). */editmain(){	register char *cp;	/*	 * Set up terminal environment.	 * The tty will be used as a "dumb" terminal if TERM is not	 *   defined in environment or if its type is not in termcap file.	 */	gettmode();	if ((cp = getenv("TERM")) != 0 && *cp) {		setterm(cp);	}	/*	 * Do global initializations, call tty setup routine	 * to get tty into raw mode, call main command parser to edit the line,	 * then restore the tty.	 */	if (COLUMNS <= 0)		COLUMNS = TUBECOLS;	/* couldn't get col size so use 80 */	if (AM)		COLUMNS--;	vi_tty_setup(1);	init_globals();	if (emacs)		emain();	else		vmain();	CP(cmdline, linebuf);	vi_tty_setup(0);}/* * This is the main routine for vi interface command decoding. * We here decode the count preceding a command * and interpret a few of the commands. * Commands which involve a target (i.e. an operator) are decoded * in the routine exoperate. */#define	forbid(a)	{ if (a) goto fonfon; }vmain(){	register int c, cnt;	/*	 * The line is in the line buffer linebuf,	 * and the cursor at the position cursor.	 */	done_edit = 0;	for (;;) {		/*		 * Decode a command.		 * Clear state for decoding of next command.		 */		vglobp = 0;		wcursor = 0;		Xhadcnt = hadcnt = 0;		Xcnt = cnt = 1;		/*		 * Gobble up counts.		 */		for (;;) {looptop:			if (isdigit(peekkey()) && peekkey() != '0') {				hadcnt = 1;				cnt = vgetcnt();				forbid (cnt <= 0);			}			break;		}reread:		/*		 * Come to reread from below after some macro expansions.		 * The call to map allows use of function key pads		 * by performing a terminal dependent mapping of inputs.		 */		op = getkey();		maphopcnt = 0;		do {			/*			 * Keep mapping the char as long as it changes.			 * This allows for double mappings, e.g., q to #,			 * #1 to something else.			 */			c = op;			op = map(c,arrows);			/*			 * Maybe the mapped to char is a count. If so, we have			 * to go back to the "for" to interpret it.			 */			if (isdigit(c) && c!='0') { /* 007 - GAG */				ungetkey(c);				goto looptop;			}			if (++maphopcnt > 256)				error("Infinite macro loop");		} while (c != op);		/*		 * Begin to build an image of this cmd in the buffer workcmd.		 */		lastcp = workcmd;		if (!vglobp)			*lastcp++ = c;		/*		 * First level command decode.		 */		switch (c) {		/*		 * ^C		Quit: with no command line.		 */		case CTRL(c):			linebuf[0] = '\0';			destcol = 0;			vclreol();			return;		/*		 * search	Edit some prior history event		 *		Search for a "word" delineated by white space.		 */		case '/':			exputchar('\/');			exflush();			if (srchback()) {				/*				 * Got an input string from user				 * If pattern found in history list				 *    edit new line.				 * else (pattern not found) restore state.				 */				if (reedit(1))					goto newedit;				else {					beep();					editevent++;					redraw();					continue;				}			} else				continue;  /* user aborted command */		/*		 * n		repeat last history search command (/).		 */		case 'n':			if (srchcmd[0] != '\0') {				/*				 * Have a search string				 * If pattern found in history list				 *    edit new line.				 * else (pattern not found) restore state.				 */				editevent--;				if (reedit(1))					goto newedit;				else {					beep();					editevent++;					/* redraw(); */					continue;				}			} else {				beep();				continue;  /* no search string */			}		/*		 * uparrow	Edit prior history event		 */		case 'k':		case CTRL(p):			editevent--;			if (reedit(0))				goto newedit;			else {	/* ran off top of history, so keep old line */				editevent++;				beep();				continue;			}		/*		 * downarrow	Edit next history event		 */		case 'j':		case CTRL(n):			editevent++;			if (reedit(0) <= 0) {				/* ran off botm of history, so keep old line */				editevent--;				beep();				continue;			}newedit:			/*			 * Reset edit state for new command line			 */			vsetcurs(linebuf);			init_globals();			continue;		/*		 * ^L,^R	Redraw the line.		 */		case CTRL(l):		case CTRL(r):			redraw();			continue;		/*		 * .		Repeat the last (modifying) command.		 */		case '.':			/*			 * Check that there was a last command, and			 * take its count.			 */			forbid (lastcmd[0] == 0);			if (hadcnt)				lastcnt = cnt;			cnt = lastcnt;			hadcnt = lasthad;			vglobp = lastcmd;			goto reread;		/*		 * $		Escape just cancels the current command		 *		with a little feedback.		 */		case ESCAPE:			beep();			continue;		/*		 * C		Change text to end of line, short for c$.		 */		case 'C':			if (*cursor) {				ungetkey('$'), c = 'c';				break;			}			goto appnd;		/*		 * ~	Switch case of letter under cursor		 */		case '~':			/*			 * Save command for repeat cmd.			 * Save old line state for undo.			 */			setLAST();			CP(oldbuf, linebuf);			oldcursor = cursor;			while (cnt > 0) {				cnt--;				if (isalpha(*cursor))					*cursor ^= ' ';	/* toggle the case */				exputchar(*cursor);				cursor++;				if (*cursor == '\0') {					cursor--;					vsetcurs(cursor);					break;				}			}			continue;		/*		 * A		Append at end of line, short for $a.		 */		case 'A':			exoperate('$', 1);appnd:			c = 'a';			/* fall into ... */		/*		 * a		Appends text after cursor.  Text can continue		 *		through arbitrary number of lines.		 */		case 'a':			if (*cursor) {				cursor++;			}			goto insrt;		/*		 * I		Insert at beginning of whitespace of line,		 *		short for ^i.		 */		case 'I':			exoperate('0', 1);			c = 'i';			/* fall into ... */		/*		 * i		Insert text to an escape in the buffer.		 *		Text is arbitrary.		 */		case 'i':insrt:			/*			 * Common code for all the insertion commands.			 * Save command for repeat cmd. Save old line state			 * for undo.  Position cursor and do append.			 * Note that nothing is doomed.			 * If NL or CR entered in insert mode (vgetline()),			 * we end insert AND end entire edit with "done_edit"			 * flag.  So flush output buffer and end edit. (flusho			 * is normally called in "getbr()" before getting a char)			 */			setLAST();			CP(oldbuf, linebuf);			oldcursor = cursor;			vgoto(column(cursor), 0);			doomed = 0;			vappend(c, cnt, 0);			if (done_edit) {				flusho();				return;		/* 006 - GAG */			}			continue;		/*		 * ZZ, NL, CR	end edit so cmd can be executed.		 */		 case 'Z':			forbid(getkey() != 'Z');		 case NL:		 case CR:			globp = "x";			return;		/* 006 - GAG */		/*		 * P		Put back text before cursor.		 *		 * p		Like P but after rather than before cursor.		 */		case 'P':		case 'p':			/*			 * Save command for repeat cmd. Save old line state			 * for undo.			 * Use an append or insert to put it back so as to			 * use insert mode.			 */			setLAST();			CP(oldbuf, linebuf);			oldcursor = cursor;			if (DEL[0]) {				forbid (DEL[0] == NULL);				vglobp = DEL;				ungetkey(c == 'p' ? 'a' : 'i');				goto reread;			}			beep();			continue;		/*		 * u		undo last change (but won't undo an undo)		 */		case 'u':			CP(linebuf, oldbuf);			/*			 * Redraw the line			 */			vsetcurs(linebuf);			normline();			/*			 * Clear from last char to end of line			 */			destcol = column(strend(linebuf)) + 1;			vclreol();			/*			 * Reset cursor			 */			cursor = oldcursor;			if (*cursor == '\0')				cursor--;			vsetcurs(cursor);			continue;		/*		 * U		undo: restore current line to initial state.		 */		case 'U':			CP(linebuf, cmdline);			/*			 * Redraw the line			 */			vsetcurs(linebuf);			normline();			/*			 * Clear from last char to end of line			 */			destcol = column(strend(linebuf)) + 1;			vclreol();			/*			 * Reset cursor to begining of line			 */			vsetcurs(linebuf);			continue;fonfon:			beep();			vmacp = 0;			continue;		} /* end switch (c) */		/*		 * Rest of commands are decoded by the exoperate		 * routine.		 *		 * If "done_edit" got set in vgetline (during change)		 * then flush output buffer and end edit. (flusho is		 * normally called in "getbr()" before getting a char)		 */		exoperate(c, cnt);		if (done_edit) {			flusho();			break;		}	}	return;}#define	blank()		isspace(wcursor[0])#undef	forbid#define	forbid(a)	if (a) goto errlab;/* * Decode an operator/operand type command. * The work here is setting up a function variable to point * to the routine we want, and manipulation of the variable * wcursor, which marks the other end of the affected * area. */exoperate(c, cnt)	register int c, cnt;{	register int i;	int (*moveop)(), (*deleteop)();	register int (*opf)();	bool subop = 0;	static char lastFKND, lastFCHR;	moveop = vmove, deleteop = vdelete;	wcursor = cursor;	dir = 1;	switch (c) {	/*	 * d		delete operator.	 */	case 'd':		CP(oldbuf, linebuf);		oldcursor = cursor;		moveop = vdelete;		deleteop = beep;		break;	/*	 * c		Change operator.	 */	case 'c':		CP(oldbuf, linebuf);		oldcursor = cursor;		if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')			subop++;		moveop = vchange;		deleteop = beep;		break;	/*	 * r		Replace character under cursor with single following	 *		character.	 */	case 'r':		CP(oldbuf, linebuf);		oldcursor = cursor;		vrep(cnt);		return;	default:		goto nocount;	}	/*	 * Had an operator, so accept another count.	 * Multiply counts together.	 */	if (isdigit(peekkey()) && peekkey() != '0') {		cnt *= vgetcnt();		Xcnt = cnt;		forbid (cnt <= 0);	}	/*	 * Get next character, mapping it and saving it.	 */	c = map(getesc(),arrows);	if (c == 0)		return;	if (!subop)		*lastcp++ = c;nocount:	opf = moveop;	switch (c) {	/*	 * b		Back up a word.	 * B		Back up a word, liberal definition.	 */	case 'b':	case 'B':		dir = -1;		/* fall into ... */	/*	 * w		Forward a word.	 * W		Forward a word, liberal definition.	 */	case 'W':	case 'w':		wdkind = c & ' ';		forbid(lfind(2, cnt, opf) < 0);		break;	/*	 * E		to end of following blank/nonblank word	 */	case 'E':		wdkind = 0;		goto ein;	/*	 * e		To end of following word.	 */	case 'e':		wdkind = 1;ein:		forbid(lfind(3, cnt - 1, opf) < 0);		break;	/*	 * %		To matching (), [], or {}.  If not at one scan for	 *		first such after cursor on this line.	 */	case '%':		i = lmatchp();		forbid(!i);		if (opf != vmove)			if (dir > 0)				wcursor++;			else				cursor++;		break;	/*	 * 0		To beginning of real line.	 */	case '0':		wcursor = linebuf;		break;	/*	 * F		Find single character before cursor in current line.	 * T		Like F, but stops before character.	 */	case 'F':	/* inverted find */	case 'T':		dir = -1;		/* fall into ... */	/*	 * f		Find single character following cursor in current line.	 * t		Like f, but stop before character.	 */	case 'f':	/* find */	case 't':		if (!subop) {			i = getesc();			if (i == 0)				return;			*lastcp++ = i;		}		if (vglobp == 0)			lastFKND = c, lastFCHR = i;		for (; cnt > 0; cnt--)			forbid (find(i) == 0);		switch (c) {		case 'T':			wcursor++;			break;		case 't':			wcursor--;		case 'f':fixup:			if (moveop != vmove)				wcursor++;			break;		}		break;	/*	 * $		To end of line.	 */	case '$':		if (cnt > 1)			cnt = 1;		if (linebuf[0]) {			wcursor = strend(linebuf) - 1;			goto fixup;		}		wcursor = linebuf;		break;	/*	 * h		Back a character.	 * ^H		Back a character.	 */	case 'h':	case CTRL(h):		dir = -1;		/* fall into ... */	/*	 * space	Forward a character.	 */	case 'l':	case ' ':		forbid (margin() || opf == vmove && edge());		while (cnt > 0 && !margin())			wcursor += dir, cnt--;		if (margin() && opf == vmove || wcursor < linebuf)			wcursor -= dir;		break;	/*	 * D		Delete to end of line, short for d$.	 */	case 'D':		cnt = INF;		goto deleteit;	/*	 * X		Delete character before cursor.	 */	case 'X':		dir = -1;		/* fall into ... */deleteit:	/*	 * x		Delete character at cursor, leaving cursor where it is.	 */	case 'x':		CP(oldbuf, linebuf);		oldcursor = cursor;		if (margin())			goto errlab;		while (cnt > 0 && !margin())			wcursor += dir, cnt--;		opf = deleteop;		break;	default:		if (opf == vmove || c != workcmd[0]) {errlab:			beep();			return;		}	}	/*	 * Apply.	 */	(*opf)(c);}/* * This is the main routine for emacs interface command decoding. * We here decode the count preceding a command and interpret the commands. */#define	eforbid(a)	{ if (a) goto efonfon; }emain(){	register int c, cnt;	register int i;	register int (*opf)();

⌨️ 快捷键说明

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