Minix1.5/commands/elvis/vi.c

Compare this file to the similar file:
Show the results in this format:

/* vi.c */

/* Author:
 *	Steve Kirkendall
 *	16820 SW Tallac Way
 *	Beaverton, OR 97006
 *	kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
 */


#include <ctype.h>
#include "vi.h"



/* This array describes what each key does */
#define NO_FUNC		(MARK (*)())0
#define NO_ARGS		0
#define CURSOR_COUNT	1
#define CURSOR		2
#define CURSOR_CNT_KEY	3
#define CURSOR_MOVED	4
#define CURSOR_EOL	5
#define ZERO		6
#define DIGIT		7
#define CURSOR_TEXT	8
#define CURSOR_CNT_CMD	9
#define KEYWORD		10
#define NO_FLAGS	0x00
#define	MVMT		0x01	/* this is a movement command */
#define PTMV		0x02	/* this can be *part* of a movement command */
#define FRNT		0x04	/* after move, go to front of line */
#define INCL		0x08	/* include last char when used with c/d/y */
#define LNMD		0x10	/* use line mode of c/d/y */
#define NCOL		0x20	/* this command can't change the column# */
#define NREL		0x40	/* this is "non-relative" -- set the '' mark */
#define SDOT		0x80	/* set the "dot" variables, for the "." cmd */
static struct keystru
{
	MARK	(*func)();	/* the function to run */
	char	args;		/* description of the args needed */
	char	flags;		/* other stuff */
}
	vikeys[] =
{
/* NUL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^A  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^B  page backward	*/	{movescroll,	CURSOR_CNT_CMD,	FRNT},
/* ^C  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^D  scroll dn 1/2page*/	{movescroll,	CURSOR_CNT_CMD,	NCOL},
/* ^E  scroll up	*/	{movescroll,	CURSOR_CNT_CMD,	NCOL},
/* ^F  page forward	*/	{movescroll,	CURSOR_CNT_CMD,	FRNT},
/* ^G  show file status	*/	{v_status,	NO_ARGS, 	NO_FLAGS},
/* ^H  move left, like h*/	{moveleft,	CURSOR_COUNT,	MVMT},
/* ^I  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^J  move down	*/	{movedown,	CURSOR_COUNT,	MVMT|LNMD},
/* ^K  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^L  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS},
/* ^M  mv front next ln */	{movedown,	CURSOR_COUNT,	MVMT|FRNT|LNMD},
/* ^N  move down	*/	{movedown,	CURSOR_COUNT,	MVMT|LNMD},
/* ^O  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^P  not defined	*/	{moveup,	CURSOR_COUNT,	MVMT|LNMD},
/* ^Q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^R  redraw screen	*/	{v_redraw,	NO_ARGS,	NO_FLAGS},
/* ^S  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^T  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^U  scroll up 1/2page*/	{movescroll,	CURSOR_CNT_CMD,	NCOL},
/* ^V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^W  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^X  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^Y  scroll down	*/	{movescroll,	CURSOR_CNT_CMD,	NCOL},
/* ^Z  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ESC not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^\  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^]  keyword is tag	*/	{v_tag,		KEYWORD,	NO_FLAGS},
/* ^^  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* ^_  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/* SPC move right,like l*/	{moveright,	CURSOR_COUNT,	MVMT},
/*  !  run thru filter	*/	{v_filter,	CURSOR_MOVED,	NO_FLAGS},
/*  "  select cut buffer*/	{v_selcut,	CURSOR_CNT_KEY,	PTMV},
/*  #  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  $  move to rear	*/	{moverear,	CURSOR,		MVMT|INCL},
/*  %  move to match	*/	{movematch,	CURSOR,		MVMT|INCL},
/*  &  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  '  move to a mark	*/	{movetomark,	CURSOR_CNT_KEY,	MVMT|FRNT|NREL|LNMD},
/*  (  mv back sentence	*/	{movebsentence,	CURSOR_COUNT,	MVMT},
/*  )  mv fwd sentence	*/	{movefsentence,	CURSOR_COUNT,	MVMT},
/*  *  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  +  mv front next ln */	{movedown,	CURSOR_COUNT,	MVMT|FRNT|LNMD},
/*  ,  reverse [fFtT] cmd*/	{move_ch,	CURSOR_CNT_CMD,	MVMT|INCL},
/*  -  mv front prev ln	*/	{moveup,	CURSOR_COUNT,	MVMT|FRNT|LNMD},
/*  .  special...	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  /  forward search	*/	{movefsrch,	CURSOR_TEXT,	MVMT|NREL},
/*  0  part of count?	*/	{NO_FUNC,	ZERO,		MVMT|PTMV},
/*  1  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  2  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  3  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  4  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  5  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  6  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  7  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  8  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  9  part of count	*/	{NO_FUNC,	DIGIT,		PTMV},
/*  :  run single EX cmd*/	{v_1ex,		CURSOR_TEXT,	NO_FLAGS},
/*  ;  repeat [fFtT] cmd*/	{move_ch,	CURSOR_CNT_CMD,	MVMT|INCL},
/*  <  shift text left	*/	{v_shiftl,	CURSOR_MOVED,	SDOT|FRNT},
/*  =  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  >  shift text right	*/	{v_shiftr,	CURSOR_MOVED,	SDOT|FRNT},
/*  ?  backward search	*/	{movebsrch,	CURSOR_TEXT,	MVMT|NREL},
/*  @  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  A  append at EOL	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  B  move back Word	*/	{movebWord,	CURSOR_COUNT,	MVMT},
/*  C  change to EOL	*/	{v_change,	CURSOR_EOL,	SDOT},
/*  D  delete to EOL	*/	{v_delete,	CURSOR_EOL,	SDOT},
/*  E  move end of Word	*/	{moveeWord,	CURSOR_COUNT,	MVMT|INCL},
/*  F  move bk to char	*/	{moveFch,	CURSOR_CNT_KEY,	MVMT|INCL},
/*  G  move to line #	*/	{movetoline,	CURSOR_COUNT,	MVMT|NREL|LNMD},
/*  H  move to row	*/	{moverow,	CURSOR_CNT_CMD,	FRNT},
/*  I  insert at front	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  J  join lines	*/	{v_join,	CURSOR_COUNT,	SDOT},
/*  K  look up keyword	*/	{v_keyword,	KEYWORD,	NO_FLAGS},
/*  L  move to last row	*/	{moverow,	CURSOR_CNT_CMD,	FRNT},
/*  M  move to mid row	*/	{moverow,	CURSOR_CNT_CMD,	FRNT},
/*  N  reverse prev srch*/	{moveNsrch,	CURSOR,		MVMT},
/*  O  insert above line*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  P  paste before	*/	{v_paste,	CURSOR_CNT_CMD,	NO_FLAGS},
/*  Q  quit to EX mode	*/	{v_quit,	NO_ARGS,	NO_FLAGS},
/*  R  overtype		*/	{v_overtype,	CURSOR,		SDOT},
/*  S  not defined	*/	{v_change,	CURSOR_MOVED,	SDOT},
/*  T  move bk to char	*/	{moveTch,	CURSOR_CNT_KEY,	MVMT|INCL},
/*  U  not defined	*/	{v_undoline,	CURSOR,		FRNT},
/*  V  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  W  move forward Word*/	{movefWord,	CURSOR_COUNT,	MVMT},
/*  X  delete to left	*/	{v_Xchar,	CURSOR_COUNT,	SDOT},
/*  Y  yank text	*/	{v_yank,	CURSOR_MOVED,	NO_FLAGS},
/*  Z  save file & exit	*/	{v_xit,		CURSOR_CNT_KEY,	NO_FLAGS},
/*  [  move back section*/	{movebsection,	CURSOR_CNT_KEY,	MVMT|LNMD|NREL},
/*  \  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  ]  move fwd section */	{movefsection,	CURSOR_CNT_KEY,	MVMT|LNMD|NREL},
/*  ^  move to front	*/	{movefront,	CURSOR,		MVMT},
/*  _  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  `  move to mark	*/	{movetomark,	CURSOR_CNT_KEY,	MVMT|NREL},
/*  a  append at cursor	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  b  move back word	*/	{movebword,	CURSOR_COUNT,	MVMT},
/*  c  change text	*/	{v_change,	CURSOR_MOVED,	SDOT},
/*  d  delete op	*/	{v_delete,	CURSOR_MOVED,	SDOT},
/*  e  move end word	*/	{moveeword,	CURSOR_COUNT,	MVMT|INCL},
/*  f  move fwd for char*/	{movefch,	CURSOR_CNT_KEY,	MVMT|INCL},
/*  g  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  h  move left	*/	{moveleft,	CURSOR_COUNT,	MVMT},
/*  i  insert at cursor	*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  j  move down	*/	{movedown,	CURSOR_COUNT,	MVMT|NCOL|LNMD},
/*  k  move up		*/	{moveup,	CURSOR_COUNT,	MVMT|NCOL|LNMD},
/*  l  move right	*/	{moveright,	CURSOR_COUNT,	MVMT},
/*  m  define a mark	*/	{v_mark,	CURSOR_CNT_KEY,	NO_FLAGS},
/*  n  repeat prev srch	*/	{movensrch,	CURSOR, 	MVMT},
/*  o  insert below line*/	{v_insert,	CURSOR_CNT_CMD,	SDOT},
/*  p  paste after	*/	{v_paste,	CURSOR_CNT_CMD,	NO_FLAGS},
/*  q  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  r  replace chars	*/	{v_replace,	CURSOR_CNT_KEY,	SDOT},
/*  s  subst N chars	*/	{v_subst,	CURSOR_COUNT,	SDOT},
/*  t  move fwd to char	*/	{movetch,	CURSOR_CNT_KEY,	MVMT|INCL},
/*  u  undo		*/	{v_undo,	CURSOR,		NO_FLAGS},
/*  v  not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
/*  w  move fwd word	*/	{movefword,	CURSOR_COUNT,	MVMT},
/*  x  delete character	*/	{v_xchar,	CURSOR_COUNT,	SDOT},
/*  y  yank text	*/	{v_yank,	CURSOR_MOVED,	NO_FLAGS},
/*  z  adjust scrn row	*/	{movez, 	CURSOR_CNT_KEY,	NCOL},
/*  {  back paragraph	*/	{movebparagraph,CURSOR_COUNT,	MVMT|LNMD},
/*  |  move to column	*/	{movetocol,	CURSOR_COUNT,	NREL},
/*  }  fwd paragraph	*/	{movefparagraph,CURSOR_COUNT,	MVMT|LNMD},
/*  ~  upper/lowercase	*/	{v_ulcase,	CURSOR,		SDOT},
/* DEL not defined	*/	{NO_FUNC,	NO_ARGS,	NO_FLAGS},
};



vi()
{
	register int		key;	/* keystroke from user */
	long			count;	/* numeric argument to some functions */
	register struct keystru	*keyptr;/* pointer to vikeys[] element */
	MARK			tcurs;	/* temporary cursor */
	int			prevkey;/* previous key, if d/c/y/</>/! */
	MARK			range;	/* start of range for d/c/y/</>/! */
	char			text[100];
	int			dotkey;	/* last "key" of a change */
	int			dotpkey;/* last "prevkey" of a change */
	int			dotkey2;/* last extra "getkey()" of a change */
	int			dotcnt;	/* last "count" of a change */
	register int		i;

	/* tell the redraw() function to start from scratch */
	redraw(MARK_UNSET, FALSE);
	msg((char *)0);

#ifdef lint
	/* lint says that "range" might be used before it is set.  This
	 * can't really happen due to the way "range" and "prevkey" are used,
	 * but lint doesn't know that.  This line is here ONLY to keep lint
	 * happy.
	 */
	range = 0L;
#endif

	/* safeguard against '.' with no previous command */
	dotkey = 0;

	/* Repeatedly handle VI commands */
	for (count = 0, prevkey = '\0'; mode == MODE_VI; )
	{
		/* if we've moved off the undoable line, then we can't undo it at all */
		if (markline(cursor) != U_line)
		{
			U_line = 0L;
		}

		/* report any changes from the previous command */
		if (rptlines >= *o_report)
		{
			redraw(cursor, FALSE);
			msg("%ld lines %s", rptlines, rptlabel);
		}
		rptlines = 0L;

		/* get the next command key.  It must be ASCII */
		do
		{
			key = getkey(WHEN_VICMD);
		} while (key < 0 || key > 127);

		/* change cw and cW commands to ce and cE, respectively */
		/* (Why?  because the real vi does it that way!) */
		if (prevkey == 'c')
		{
			if (key == 'w')
				key = 'e';
			else if (key == 'W')
				key = 'E';

			/* wouldn't work right at the end of a word unless we
			 * backspace one character before doing the move.  This
			 * will fix most cases.
			 */
			if (markidx(cursor) > 0 && (key == 'e' || key == 'E'))
			{
				cursor--;
			}
		}

		/* look up the structure describing this command */
		keyptr = &vikeys[key];

		/* if we're in the middle of a d/c/y/</>/! command, reject
		 * anything but movement or a doubled version like "dd".
		 */
		if (prevkey && key != prevkey && !(keyptr->flags & (MVMT|PTMV)))
		{
			beep();
			prevkey = 0;
			count = 0;
			continue;
		}

		/* set the "dot" variables, if we're supposed to */
		if ((keyptr->flags & SDOT)
		 || (prevkey && vikeys[prevkey].flags & SDOT))
		{
			dotkey = key;
			dotpkey = prevkey;
			dotkey2 = '\0';
			dotcnt = count;

			/* remember the line before any changes are made */
			if (U_line != markline(cursor))
			{
				U_line = markline(cursor);
				strcpy(U_text, fetchline(U_line));
			}
		}

		/* if this is "." then set other vars from the "dot" vars */
		if (key == '.')
		{
			key = dotkey;
			keyptr = &vikeys[key];
			prevkey = dotpkey;
			if (prevkey)
			{
				range = cursor;
			}
			if (count == 0)
			{
				count = dotcnt;
			}
			doingdot = TRUE;

			/* remember the line before any changes are made */
			if (U_line != markline(cursor))
			{
				U_line = markline(cursor);
				strcpy(U_text, fetchline(U_line));
			}
		}
		else
		{
			doingdot = FALSE;
		}

		/* process the key as a command */
		tcurs = cursor;
		switch (keyptr->args)
		{
		  case ZERO:
			if (count == 0)
			{
				tcurs = cursor & ~(BLKSIZE - 1);
				break;
			}
			/* else fall through & treat like other digits... */

		  case DIGIT:
			count = count * 10 + key - '0';
			break;

		  case KEYWORD:
			/* if not on a keyword, fail */
			pfetch(markline(cursor));
			key = markidx(cursor);
			if (!isalnum(ptext[key]) && ptext[key] != '_')
			{
				tcurs = MARK_UNSET;
				break;
			}

			/* find the start of the keyword */
			while (key > 0 && (isalnum(ptext[key - 1]) || ptext[key - 1] == '_'))
			{
				key--;
			}

			/* copy it into a buffer, and NUL-terminate it */
			i = 0;
			do
			{
				text[i++] = ptext[key++];
			} while (isalnum(ptext[key]) || ptext[key] == '_');
			text[i] = '\0';

			/* call the function */
			tcurs = (*keyptr->func)(text);
			count = 0L;
			break;

		  case NO_ARGS:
			if (keyptr->func)
			{
				(*keyptr->func)();
			}
			else
			{
				beep();
			}
			count = 0L;
			break;
	
		  case CURSOR_COUNT:
			tcurs = (*keyptr->func)(cursor, count);
			count = 0L;
			break;
	
		  case CURSOR:
			tcurs = (*keyptr->func)(cursor);
			count = 0L;
			break;

		  case CURSOR_CNT_KEY:
			if (doingdot)
			{
				tcurs = (*keyptr->func)(cursor, count, dotkey2);
			}
			else if (keyptr->flags & SDOT
			 || (prevkey && vikeys[prevkey].flags & SDOT))
			{
				dotkey2 = getkey(0);
				tcurs = (*keyptr->func)(cursor, count, dotkey2);
			}
			else
			{
				tcurs = (*keyptr->func)(cursor, count, getkey(0));
			}
			count = 0L;
			break;
	
		  case CURSOR_MOVED:
			/* uppercase keys always act like doubled */
			if (isupper(key))
			{
				prevkey = key;
				range = cursor;
			}

			if (prevkey)
			{
				/* doubling up a command, use complete lines */
				range &= ~(BLKSIZE - 1);
				if (count)
				{
					tcurs = range + MARK_AT_LINE(count);
					count = 0;
				}
				else
				{
					tcurs = range + BLKSIZE;
				}
			}
			else
			{
				prevkey = key;
				range = cursor;
				key = -1; /* so we don't think we doubled yet */
			}
			break;

		  case CURSOR_EOL:
			/* act like CURSOR_MOVED with '$' movement */
			range = cursor;
			tcurs = moverear(cursor, 1L);
			count = 0L;
			prevkey = key;
			key = '$';
			keyptr = &vikeys['$'];
			break;

		  case CURSOR_TEXT:
			if (vgets(key, text, sizeof text) >= 0)
			{
				/* reassure user that <CR> was hit */
				qaddch('\r');
				refresh();

				/* call the function with the text */
				tcurs = (*keyptr->func)(cursor, text);
			}
			count = 0L;
			break;

		  case CURSOR_CNT_CMD:
			tcurs = (*keyptr->func)(cursor, count, key);
			count = 0L;
			break;
		}

		/* if that command took us out of vi mode, then exit the loop
		 * NOW, without tweaking the cursor or anything.  This is very
		 * important when mode == MODE_QUIT.
		 */
		if (mode != MODE_VI)
		{
			break;
		}

		/* now move the cursor, as appropriate */
		if (prevkey && markline(tcurs) > nlines)
		{
			/* destination for operator may be nlines + 1 */
			cursor = MARK_AT_LINE(nlines + 1);
		}
		else if (keyptr->args == CURSOR_MOVED)
		{
			/* the < and > keys have FRNT,
			 * but it shouldn't be applied yet
			 */
			cursor = adjmove(cursor, tcurs, 0);
		}
		else
		{
			cursor = adjmove(cursor, tcurs, keyptr->flags);
		}

		/* was that the end of a d/c/y/</>/! command? */
		if (prevkey && (prevkey == key || (keyptr->flags & MVMT)))
		{
			/* if the movement command failed, cancel operation */
			if (tcurs == MARK_UNSET)
			{
				prevkey = 0;
				count = 0;
				continue;
			}

			/* make sure range=front and tcurs=rear */
			if (cursor < range)
			{
				tcurs = range;
				range = cursor;
			}
			else
			{
				tcurs = cursor;
			}

			/* adjust for line mode */
			if (keyptr->flags & LNMD)
			{
				range &= ~(BLKSIZE - 1);
				tcurs &= ~(BLKSIZE - 1);
				tcurs += BLKSIZE;
			}

			/* adjust for inclusion of last char */
			if (keyptr->flags & INCL)
			{
				tcurs++;
			}

			/* temporarily move the cursor to "range" so that
			 * beforedo() remembers the cursor's real location.
			 * This is important if the user later does undo()
			 */
			cursor = range;

			/* run the function */
			tcurs = (*vikeys[prevkey].func)(range, tcurs);
			cursor = adjmove(cursor, tcurs, vikeys[prevkey].flags);

			/* cleanup */
			prevkey = 0;
		}
	}
}

/* This function adjusts the MARK value that they return; here we make sure
 * it isn't past the end of the line, and that the column hasn't been
 * *accidentally* changed.
 */
MARK adjmove(old, new, flags)
	MARK		old;	/* the cursor position before the command */
	register MARK	new;	/* the cursor position after the command */
	int		flags;	/* various flags regarding cursor mvmt */
{
	static int	colno;	/* the column number that we want */
	register char	*text;	/* used to scan through the line's text */
	register int	i;

	/* if the command failed, bag it! */
	if (new == MARK_UNSET)
	{
		beep();
		return old;
	}

	/* if this is a non-relative movement, set the '' mark */
	if (flags & NREL)
	{
		mark[26] = old;
	}

	/* make sure it isn't past the end of the file */
	if (markline(new) < 1)
	{
		new = MARK_FIRST;
	}
	else if (markline(new) > nlines)
	{
		new = MARK_LAST;
	}

	/* fetch the new line */
	pfetch(markline(new));

	/* move to the front, if we're supposed to */
	if (flags & FRNT)
	{
		new = movefront(new, 1L);
	}

	/* change the column#, or change the mark to suit the column# */
	if (!(flags & NCOL))
	{
		/* change the column# */
		i = markidx(new);
		if (i == BLKSIZE - 1)
		{
			new &= ~(BLKSIZE - 1);
			if (plen > 0)
			{
				new += plen - 1;
			}
			colno = BLKSIZE * 8; /* one heck of a big colno */
		}
		else if (plen > 0)
		{
			if (i >= plen)
			{
				new = (new & ~(BLKSIZE - 1)) + plen - 1;
			}
			colno = idx2col(new, ptext, FALSE);
		}
		else
		{
			new &= ~(BLKSIZE - 1);
			colno = 0;
		}
	}
	else
	{
		/* adjust the mark to get as close as possible to column# */
		for (i = 0, text = ptext; i <= colno && *text; text++)
		{
			if (*text == '\t')
			{
				i += *o_tabstop - (i % *o_tabstop);
			}
			else if (*text > 0 && *text < ' ' || *text == 127)
			{
				i += 2;
			}
#ifndef SET_NOCHARATTR
			else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
			{
				text += 2; /* plus one more in "for()" stmt */
			}
#endif
			else
			{
				i++;
			}
		}
		if (text > ptext)
		{
			text--;
		}
		new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
	}

	return new;
}