586 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			586 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /* move1.c */
 | |
| 
 | |
| /* Author:
 | |
|  *	Steve Kirkendall
 | |
|  *	14407 SW Teal Blvd. #C
 | |
|  *	Beaverton, OR 97005
 | |
|  *	kirkenda@cs.pdx.edu
 | |
|  */
 | |
| 
 | |
| 
 | |
| /* This file contains most movement functions */
 | |
| 
 | |
| #include "config.h"
 | |
| #include "vi.h"
 | |
| #include "ctype.h"
 | |
| 
 | |
| MARK	m_updnto(m, cnt, cmd)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* a numeric argument */
 | |
| 	char	cmd;	/* the command character */
 | |
| {
 | |
| 	DEFAULT(cmd == 'G' ? nlines : 1L);
 | |
| 
 | |
| 	/* move up or down 'cnt' lines */
 | |
| 	switch (cmd)
 | |
| 	{
 | |
| 	  case ctrl('P'):
 | |
| 	  case '-':
 | |
| 	  case 'k':
 | |
| 		m -= MARK_AT_LINE(cnt);
 | |
| 		break;
 | |
| 
 | |
| 	  case 'G':
 | |
| 		if (cnt < 1L || cnt > nlines)
 | |
| 		{
 | |
| 			msg("Only %ld lines", nlines);
 | |
| 			return MARK_UNSET;
 | |
| 		}
 | |
| 		m = MARK_AT_LINE(cnt);
 | |
| 		break;
 | |
| 
 | |
| 	  case '_':
 | |
| 		cnt--;
 | |
| 		/* fall through... */
 | |
| 
 | |
| 	  default:
 | |
| 		m += MARK_AT_LINE(cnt);
 | |
| 	}
 | |
| 
 | |
| 	/* if that left us screwed up, then fail */
 | |
| 	if (m < MARK_FIRST || markline(m) > nlines)
 | |
| 	{
 | |
| 		return MARK_UNSET;
 | |
| 	}
 | |
| 
 | |
| 	return m;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| MARK	m_right(m, cnt, key, prevkey)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* a numeric argument */
 | |
| 	int	key;	/* movement keystroke */
 | |
| 	int	prevkey;/* operator keystroke, or 0 if none */
 | |
| {
 | |
| 	int		idx;	/* index of the new cursor position */
 | |
| 
 | |
| 	DEFAULT(1);
 | |
| 
 | |
| 	/* If used with an operator, then move 1 less character, since the 'l'
 | |
| 	 * command includes the character that it moves onto.
 | |
| 	 */
 | |
| 	if (prevkey != '\0')
 | |
| 	{
 | |
| 		cnt--;
 | |
| 	}
 | |
| 
 | |
| 	/* move to right, if that's OK */
 | |
| 	pfetch(markline(m));
 | |
| 	idx = markidx(m) + cnt;
 | |
| 	if (idx < plen)
 | |
| 	{
 | |
| 		m += cnt;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		return MARK_UNSET;
 | |
| 	}
 | |
| 
 | |
| 	return m;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| MARK	m_left(m, cnt)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* a numeric argument */
 | |
| {
 | |
| 	DEFAULT(1);
 | |
| 
 | |
| 	/* move to the left, if that's OK */
 | |
| 	if (markidx(m) >= cnt)
 | |
| 	{
 | |
| 		m -= cnt;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		return MARK_UNSET;
 | |
| 	}
 | |
| 
 | |
| 	return m;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| MARK	m_tocol(m, cnt, cmd)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* a numeric argument */
 | |
| 	int	cmd;	/* either ctrl('X') or '|' */
 | |
| {
 | |
| 	char	*text;	/* text of the line */
 | |
| 	int	col;	/* column number */
 | |
| 	int	idx;	/* index into the line */
 | |
| 
 | |
| 
 | |
| 	/* if doing ^X, then adjust for sideways scrolling */
 | |
| 	if (cmd == ctrl('X'))
 | |
| 	{
 | |
| 		DEFAULT(*o_columns & 0xff);
 | |
| 		cnt += leftcol;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		DEFAULT(1);
 | |
| 	}
 | |
| 
 | |
| 	/* internally, columns are numbered 0..COLS-1, not 1..COLS */
 | |
| 	cnt--;
 | |
| 
 | |
| 	/* if 0, that's easy */
 | |
| 	if (cnt == 0)
 | |
| 	{
 | |
| 		m &= ~(BLKSIZE - 1);
 | |
| 		return m;
 | |
| 	}
 | |
| 
 | |
| 	/* find that column within the line */
 | |
| 	pfetch(markline(m));
 | |
| 	text = ptext;
 | |
| 	for (col = idx = 0; col < cnt && *text; text++, idx++)
 | |
| 	{
 | |
| 		if (*text == '\t' && !*o_list)
 | |
| 		{
 | |
| 			col += *o_tabstop;
 | |
| 			col -= col % *o_tabstop;
 | |
| 		}
 | |
| 		else if (UCHAR(*text) < ' ' || *text == '\177')
 | |
| 		{
 | |
| 			col += 2;
 | |
| 		}
 | |
| #ifndef NO_CHARATTR
 | |
| 		else if (text[0] == '\\' && text[1] == 'f' && text[2] && *o_charattr)
 | |
| 		{
 | |
| 			text += 2; /* plus one more as part of for loop */
 | |
| 		}
 | |
| #endif
 | |
| 		else
 | |
| 		{
 | |
| 			col++;
 | |
| 		}
 | |
| 	}
 | |
| 	if (!*text)
 | |
| 	{
 | |
| 		/* the desired column was past the end of the line, so
 | |
| 		 * act like the user pressed "$" instead.
 | |
| 		 */
 | |
| 		return m | (BLKSIZE - 1);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		m = (m & ~(BLKSIZE - 1)) + idx;
 | |
| 	}
 | |
| 	return m;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| MARK	m_front(m, cnt)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* a numeric argument (ignored) */
 | |
| {
 | |
| 	char	*scan;
 | |
| 
 | |
| 	/* move to the first non-whitespace character */
 | |
| 	pfetch(markline(m));
 | |
| 	scan = ptext;
 | |
| 	m &= ~(BLKSIZE - 1);
 | |
| 	while (*scan == ' ' || *scan == '\t')
 | |
| 	{
 | |
| 		scan++;
 | |
| 		m++;
 | |
| 	}
 | |
| 
 | |
| 	return m;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| MARK	m_rear(m, cnt)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* a numeric argument (ignored) */
 | |
| {
 | |
| 	/* Try to move *EXTREMELY* far to the right.  It is fervently hoped
 | |
| 	 * that other code will convert this to a more reasonable MARK before
 | |
| 	 * anything tries to actually use it.  (See adjmove() in vi.c)
 | |
| 	 */
 | |
| 	return m | (BLKSIZE - 1);
 | |
| }
 | |
| 
 | |
| #ifndef NO_SENTENCE
 | |
| static int isperiod(ptr)
 | |
| 	char	*ptr;	/* pointer to possible sentence-ender */
 | |
| {
 | |
| 	/* if not '.', '?', or '!', then it isn't a sentence ender */
 | |
| 	if (*ptr != '.' && *ptr != '?' && *ptr != '!')
 | |
| 	{
 | |
| 		return FALSE;
 | |
| 	}
 | |
| 
 | |
| 	/* skip any intervening ')', ']', or '"' characters */
 | |
| 	do
 | |
| 	{
 | |
| 		ptr++;
 | |
| 	} while (*ptr == ')' || *ptr == ']' || *ptr == '"');
 | |
| 
 | |
| 	/* do we have two spaces or EOL? */
 | |
| 	if (!*ptr || ptr[0] == ' ' && ptr[1] == ' ')
 | |
| 	{
 | |
| 		return TRUE;
 | |
| 	}
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| MARK	m_sentence(m, cnt, cmd)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* a numeric argument */
 | |
| 	int	cmd;	/* either '(' or ')' */
 | |
| {
 | |
| 	REG char	*text;
 | |
| 	REG long	l;
 | |
| 
 | |
| 	DEFAULT(1);
 | |
| 
 | |
| 	/* If '(' command, then move back one word, so that if we hit '(' at
 | |
| 	 * the start of a sentence we don't simply stop at the end of the
 | |
| 	 * previous sentence and bounce back to the start of this one again.
 | |
| 	 */
 | |
| 	if (cmd == '(')
 | |
| 	{
 | |
| 		m = m_bword(m, 1L, 'b');
 | |
| 		if (!m)
 | |
| 		{
 | |
| 			return m;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* get the current line */
 | |
| 	l = markline(m);
 | |
| 	pfetch(l);
 | |
| 	text = ptext + markidx(m);
 | |
| 
 | |
| 	/* for each requested sentence... */
 | |
| 	while (cnt-- > 0)
 | |
| 	{
 | |
| 		/* search forward for one of [.?!] followed by spaces or EOL */
 | |
| 		do
 | |
| 		{
 | |
| 			if (cmd == ')')
 | |
| 			{
 | |
| 				/* move forward, wrap at end of line */
 | |
| 				if (!text[0])
 | |
| 				{
 | |
| 					if (l >= nlines)
 | |
| 					{
 | |
| 						return MARK_UNSET;
 | |
| 					}
 | |
| 					l++;
 | |
| 					pfetch(l);
 | |
| 					text = ptext;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					text++;
 | |
| 				}
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				/* move backward, wrap at beginning of line */
 | |
| 				if (text == ptext)
 | |
| 				{
 | |
| 					do
 | |
| 					{
 | |
| 						if (l <= 1)
 | |
| 						{
 | |
| 							return MARK_FIRST;
 | |
| 						}
 | |
| 						l--;
 | |
| 						pfetch(l);
 | |
| 					} while (!*ptext);
 | |
| 					text = ptext + plen - 1;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					text--;
 | |
| 				}
 | |
| 			}
 | |
| 		} while (!isperiod(text));
 | |
| 	}
 | |
| 
 | |
| 	/* construct a mark for this location */
 | |
| 	m = buildmark(text);
 | |
| 
 | |
| 	/* move forward to the first word of the next sentence */
 | |
| 	m = m_fword(m, 1L, 'w', '\0');
 | |
| 
 | |
| 	return m;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| MARK	m_paragraph(m, cnt, cmd)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* a numeric argument */
 | |
| 	int	cmd;	/* either '{' or '}' */
 | |
| {
 | |
| 	char	*text;	/* text of the current line */
 | |
| 	char	*pscn;	/* used to scan thru value of "paragraphs" option */
 | |
| 	long	l, ol;	/* current line number, original line number */
 | |
| 	int	dir;	/* -1 if we're moving up, or 1 if down */
 | |
| 	char	col0;	/* character to expect in column 0 */
 | |
| #ifndef NO_SENTENCE
 | |
| # define SENTENCE(x)	(x)
 | |
| 	char	*list;	/* either o_sections or o_paragraph */
 | |
| #else
 | |
| # define SENTENCE(x)
 | |
| #endif
 | |
| 
 | |
| 	DEFAULT(1);
 | |
| 
 | |
| 	/* set the direction, based on the command */
 | |
| 	switch (cmd)
 | |
| 	{
 | |
| 	  case '{':
 | |
| 		dir = -1;
 | |
| 		col0 = '\0';
 | |
| 		SENTENCE(list = o_paragraphs); 
 | |
| 		break;
 | |
| 
 | |
| 	  case '}':
 | |
| 		dir = 1;
 | |
| 		col0 = '\0';
 | |
| 		SENTENCE(list = o_paragraphs); 
 | |
| 		break;
 | |
| 
 | |
| 	  case '[':
 | |
| 		if (getkey(0) != '[')
 | |
| 		{
 | |
| 			return MARK_UNSET;
 | |
| 		}
 | |
| 		dir = -1;
 | |
| 		col0 = '{';
 | |
| 		SENTENCE(list = o_sections); 
 | |
| 		break;
 | |
| 
 | |
| 	  case ']':
 | |
| 		if (getkey(0) != ']')
 | |
| 		{
 | |
| 			return MARK_UNSET;
 | |
| 		}
 | |
| 		dir = 1;
 | |
| 		col0 = '{';
 | |
| 		SENTENCE(list = o_sections); 
 | |
| 		break;
 | |
| 	}
 | |
| 	ol = l = markline(m);
 | |
| 
 | |
| 	/* for each paragraph that we want to travel through... */
 | |
| 	while (l > 0 && l <= nlines && cnt-- > 0)
 | |
| 	{
 | |
| 		/* skip blank lines between paragraphs */
 | |
| 		while (l > 0 && l <= nlines && col0 == *(text = fetchline(l)))
 | |
| 		{
 | |
| 			l += dir;
 | |
| 		}
 | |
| 
 | |
| 		/* skip non-blank lines that aren't paragraph separators
 | |
| 		 */
 | |
| 		do
 | |
| 		{
 | |
| #ifndef NO_SENTENCE
 | |
| 			if (*text == '.' && l != ol)
 | |
| 			{
 | |
| 				for (pscn = list; pscn[0] && pscn[1]; pscn += 2)
 | |
| 				{
 | |
| 					if (pscn[0] == text[1] && pscn[1] == text[2])
 | |
| 					{
 | |
| 						pscn = (char *)0;
 | |
| 						goto BreakBreak;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| #endif
 | |
| 			l += dir;
 | |
| 		} while (l > 0 && l <= nlines && col0 != *(text = fetchline(l)));
 | |
| BreakBreak:	;
 | |
| 	}
 | |
| 
 | |
| 	if (l > nlines)
 | |
| 	{
 | |
| 		m = MARK_LAST;
 | |
| 	}
 | |
| 	else if (l <= 0)
 | |
| 	{
 | |
| 		m = MARK_FIRST;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		m = MARK_AT_LINE(l);
 | |
| 	}
 | |
| 	return m;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| MARK	m_match(m, cnt)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* a numeric argument (normally 0) */
 | |
| {
 | |
| 	long	l;
 | |
| 	REG char	*text;
 | |
| 	REG char	match;
 | |
| 	REG char	nest;
 | |
| 	REG int		count;
 | |
| 
 | |
| #ifndef NO_EXTENSIONS
 | |
| 	/* if we're given a number, then treat it as a percentage of the file */
 | |
| 	if (cnt > 0)
 | |
| 	{
 | |
| 		/* make sure it is a reasonable number */
 | |
| 		if (cnt > 100)
 | |
| 		{
 | |
| 			msg("can only be from 1%% to 100%%");
 | |
| 			return MARK_UNSET;
 | |
| 		}
 | |
| 
 | |
| 		/* return the appropriate line number */
 | |
| 		l = (nlines - 1L) * cnt / 100L + 1L;
 | |
| 		return MARK_AT_LINE(l);
 | |
| 	}
 | |
| #endif /* undef NO_EXTENSIONS */
 | |
| 
 | |
| 	/* get the current line */
 | |
| 	l = markline(m);
 | |
| 	pfetch(l);
 | |
| 	text = ptext + markidx(m);
 | |
| 
 | |
| 	/* search forward within line for one of "[](){}" */
 | |
| 	for (match = '\0'; !match && *text; text++)
 | |
| 	{
 | |
| 		/* tricky way to recognize 'em in ASCII */
 | |
| 		nest = *text;
 | |
| 		if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[')
 | |
| 		{
 | |
| 			match = nest ^ ('[' ^ ']');
 | |
| 		}
 | |
| 		else if ((nest & 0xfe) == '(')
 | |
| 		{
 | |
| 			match = nest ^ ('(' ^ ')');
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			match = 0;
 | |
| 		}
 | |
| 	}
 | |
| 	if (!match)
 | |
| 	{
 | |
| 		return MARK_UNSET;
 | |
| 	}
 | |
| 	text--;
 | |
| 
 | |
| 	/* search forward or backward for match */
 | |
| 	if (match == '(' || match == '[' || match == '{')
 | |
| 	{
 | |
| 		/* search backward */
 | |
| 		for (count = 1; count > 0; )
 | |
| 		{
 | |
| 			/* wrap at beginning of line */
 | |
| 			if (text == ptext)
 | |
| 			{
 | |
| 				do
 | |
| 				{
 | |
| 					if (l <= 1L)
 | |
| 					{
 | |
| 						return MARK_UNSET;
 | |
| 					}
 | |
| 					l--;
 | |
| 					pfetch(l);
 | |
| 				} while (!*ptext);
 | |
| 				text = ptext + plen - 1;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				text--;
 | |
| 			}
 | |
| 
 | |
| 			/* check the char */
 | |
| 			if (*text == match)
 | |
| 				count--;
 | |
| 			else if (*text == nest)
 | |
| 				count++;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/* search forward */
 | |
| 		for (count = 1; count > 0; )
 | |
| 		{
 | |
| 			/* wrap at end of line */
 | |
| 			if (!*text)
 | |
| 			{
 | |
| 				if (l >= nlines)
 | |
| 				{
 | |
| 					return MARK_UNSET;
 | |
| 				}
 | |
| 				l++;
 | |
| 				pfetch(l);
 | |
| 				text = ptext;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				text++;
 | |
| 			}
 | |
| 
 | |
| 			/* check the char */
 | |
| 			if (*text == match)
 | |
| 				count--;
 | |
| 			else if (*text == nest)
 | |
| 				count++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* construct a mark for this place */
 | |
| 	m = buildmark(text);
 | |
| 	return m;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| MARK	m_tomark(m, cnt, key)
 | |
| 	MARK	m;	/* movement is relative to this mark */
 | |
| 	long	cnt;	/* (ignored) */
 | |
| 	int	key;	/* keystroke - the mark to move to */
 | |
| {
 | |
| 	/* mark '' is a special case */
 | |
| 	if (key == '\'' || key == '`')
 | |
| 	{
 | |
| 		if (mark[26] == MARK_UNSET)
 | |
| 		{
 | |
| 			return MARK_FIRST;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			return mark[26];
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* if not a valid mark number, don't move */
 | |
| 	if (key < 'a' || key > 'z')
 | |
| 	{
 | |
| 		return MARK_UNSET;
 | |
| 	}
 | |
| 
 | |
| 	/* return the selected mark -- may be MARK_UNSET */
 | |
| 	if (!mark[key - 'a'])
 | |
| 	{
 | |
| 		msg("mark '%c is unset", key);
 | |
| 	}
 | |
| 	return mark[key - 'a'];
 | |
| }
 | |
| 
 | 
