4.4BSD/usr/src/contrib/nvi/nvi/svi/svi_line.c

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

/*-
 * Copyright (c) 1993
 *	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 lint
static char sccsid[] = "@(#)svi_line.c	8.1 (Berkeley) 6/9/93";
#endif /* not lint */

#include <sys/types.h>

#include <curses.h>
#include <string.h>

#include "vi.h"
#include "svi_screen.h"

#if DEBUG && 0
#define	TABCH	'-'
#define	TABSTR	"--------------------"
#else
#define	TABSTR	"                    "
#define	TABCH	' '
#endif

/*
 * svi_line --
 *	Update one line on the screen.  One nasty little side effect is
 *	that it returns the screen position for the current character.
 *	Not pretty, but this is the only routine that really knows what's
 *	out there.
 * XXX
 * 	Should cache offset into last line for last screen -- this should
 *	speed up folded lines a lot.  The problem is that if a tab is broken
 *	across the line, it's going to be tricky.  Also, are there really
 *	enough folded lines that this is worthwhile?
 */
int
svi_line(sp, ep, smp, p, len, yp, xp)
	SCR *sp;
	EXF *ep;
	SMAP *smp;
	register char *p;
	size_t len, *xp, *yp;
{
	CHNAME *cname;
	recno_t lno;
	size_t chlen, cols_per_screen, cno_cnt, count_cols, last_count_cols;
	size_t offset_in_char, skip_screens;
	int ch, listset, partial, reverse_video;
	char nbuf[10];

#if DEBUG && 0
	TRACE(sp, "svi_line: row %u: line: %u off: %u\n",
	    smp - HMAP, smp->lno, smp->off);
#endif
	/* Move to the line. */
	MOVE(sp, smp - HMAP, 0);

	/* Get the character map. */
	cname = sp->cname;

	/*
	 * Special case if we're printing the info/mode line.  Skip printing
	 * the leading number, as well as other minor setup.  If painting the
	 * line between two screens, it's always in reverse video.  The only
	 * time this code paints the mode line is when the user is entering
	 * text for a ":" command, so we can put the code here instead of
	 * dealing with the empty line logic below.  This is a kludge, but it's
	 * pretty much confined to this module.
	 *
	 * Set the number of screens to skip until a character is displayed.
	 * Left-right screens are special, because we don't bother building
	 * a buffer to be skipped over.
	 */
	reverse_video = 0;
	if (ISINFOLINE(sp, smp)) {
		if (sp->child != NULL) {
			reverse_video = 1;
			standout();
		}
		listset = 0;
		if (O_ISSET(sp, O_LEFTRIGHT))
			skip_screens = 0;
		else
			skip_screens = smp->off - 1;
	} else {
		listset = O_ISSET(sp, O_LIST);
		skip_screens = smp->off - 1;
	}

	/*
	 * If O_NUMBER is set and this is the first screen of a folding
	 * line or any left-right line, display the line number.  Set
	 * the number of columns for this screen.
	 */
	if (O_ISSET(sp, O_NUMBER) && !ISINFOLINE(sp, smp)) {
		cols_per_screen = sp->cols - O_NUMBER_LENGTH;
		if (skip_screens == 0) {
			(void)snprintf(nbuf,
			    sizeof(nbuf), O_NUMBER_FMT, smp->lno);
			ADDSTR(nbuf);
		}
	} else
		cols_per_screen = sp->cols;

	/*
	 * Get a copy of the line.  Special case non-existent lines and the
	 * first line of an empty file.  In both cases, the cursor position
	 * is 0.
	 */
	if (p == NULL)
		p = file_gline(sp, ep, smp->lno, &len);
	if (p == NULL || len == 0) {
		if (yp != NULL && smp->lno == sp->lno) {
			*yp = smp - HMAP;
			*xp = O_ISSET(sp, O_NUMBER) ? O_NUMBER_LENGTH : 0;
		}
		if (file_lline(sp, ep, &lno))
			return (1);
		if (smp->lno > lno) {
			ADDCH(smp->lno == 1 ?
			    listset && skip_screens == 0 ? '$' : ' ' : '~');
		} else if (p == NULL) {
			GETLINE_ERR(sp, smp->lno);
			return (1);
		} else if (listset && skip_screens == 0)
			ADDCH('$');
		clrtoeol();
		return (0);
	}

	/*
	 * Set the number of characters to skip before reach the cursor
	 * character.  Offset by 1 and use 0 as a flag value.  We may be
	 * called repeatedly with a valid pointer to a cursor position.
	 * Don't fill it in unless it's the right line.
	 */
	cno_cnt = yp == NULL || smp->lno != sp->lno ? 0 : sp->cno + 1;

	/* This is the loop that actually displays characters. */
	for (count_cols = last_count_cols = 0, partial = 0; len; --len) {
		/* Get the next character and figure out its length. */
		if ((ch = *(u_char *)p++) == '\t' && !listset)
			chlen = TAB_OFF(sp, count_cols);
		else
			chlen = cname[ch].len;
		last_count_cols = count_cols;
		count_cols += chlen;

		/*
		 * If skipping screens, see if crossed a screen boundary.  If
		 * so, and this is the last one to skip, start displaying the
		 * characters, assuming there's something to display.
		 */
		if (skip_screens) {
			if (count_cols < cols_per_screen) {
				if (cno_cnt)
					--cno_cnt;
				continue;
			}
			count_cols -= cols_per_screen;
			cols_per_screen = sp->cols;
			if (--skip_screens || !count_cols) {
				if (cno_cnt)
					--cno_cnt;
				continue;
			}
			offset_in_char = chlen - count_cols;
			chlen = count_cols;
		} else
			offset_in_char = 0;

		/*
		 * Only display up to the right-hand column, once we there
		 * we're done.  Set a flag if the entire character wasn't
		 * displayed for use in setting the cursor.
		 */
		if (count_cols >= cols_per_screen) {
			chlen -= count_cols - cols_per_screen;
			if (count_cols > cols_per_screen)
				partial = 1;
			len = 1;		/* XXX 1, not 0, for loop. */
		}

		/*
		 * If the caller wants the cursor value, and this was the
		 * cursor character, set the value.  There are two ways to
		 * put the cursor on a tab -- if it's normal display mode,
		 * it goes on the last "space" of the tab.  If it's input
		 * mode, it goes on the first.  All other characters only
		 * set the cursor if the entire character was displayed,
		 * as the cursor goes on the last "space" of the character.
		 */
		if (cno_cnt && --cno_cnt == 0) {
			*yp = smp - HMAP;
			if (F_ISSET(sp, S_INPUT))
				*xp = last_count_cols;
			else if (!partial) {
				*xp = count_cols - 1;
				if (O_ISSET(sp, O_NUMBER) &&
				    !ISINFOLINE(sp, smp) && smp->off == 1)
					*xp += O_NUMBER_LENGTH;
			}
		}

		/*
		 * Display the character.  If it's a tab and tabs aren't some
		 * ridiculous length, do it fast.  (We do tab expansion here
		 * because curses doesn't have a way to set the tab length.)
		 */
		if (ch == '\t' && !listset) {
			chlen -= offset_in_char;
			if (chlen <= sizeof(TABSTR) - 1) {
				ADDNSTR(TABSTR, chlen);
			} else
				while (chlen--)
					ADDCH(TABCH);
		} else
			ADDNSTR(cname[ch].name + offset_in_char, chlen);
	}

	/*
	 * If not the info/mode line, and O_LIST set, and at the end of
	 * the line, and the line ended on this screen, add a trailing $.
	 */
	if (listset && len == 0 &&
	    skip_screens == 0 && count_cols < cols_per_screen) {
		++count_cols;
		ADDCH('$');
	}

	/* If didn't paint the whole line, clear the rest of it. */
	if (count_cols < cols_per_screen)
		clrtoeol();

	if (reverse_video)
		standend();
	return (0);
}

/*
 * svi_screens --
 *	Return the number of screens required by the line, or,
 *	if a column is specified, by the column within the line.
 */
size_t
svi_screens(sp, ep, lno, cnop)
	SCR *sp;
	EXF *ep;
	recno_t lno;
	size_t *cnop;
{
	size_t cols, len;
	char *p;

	/* Get a copy of the line. */
	if ((p = file_gline(sp, ep, lno, &len)) == NULL || len == 0)
		return (1);

	/* Figure out how many columns the line/column needs. */
	cols = svi_ncols(sp, p, len, cnop);

	/* Leading number if O_NUMBER option set. */
	if (O_ISSET(sp, O_NUMBER))
		cols += O_NUMBER_LENGTH;

	/* Trailing '$' if O_LIST option set. */
	if (O_ISSET(sp, O_LIST) && cnop == NULL)
		cols += sp->cname['$'].len;

	return (cols / sp->cols + (cols % sp->cols ? 1 : 0));
}

/*
 * svi_ncols --
 *	Return the number of columns required by the line, or,
 *	if a column is specified, by the column within the line.
 */
size_t
svi_ncols(sp, p, len, cnop)
	SCR *sp;
	u_char *p;
	size_t len, *cnop;
{
	CHNAME *cname;
	size_t cno_cnt, scno;
	int ch;

	/* Check for column count. */
	if (cnop != NULL)
		cno_cnt = *cnop;

	/* Calculate the columns needed. */
	cname = sp->cname;
	for (scno = 0; len; --len) {
		if ((ch = *(u_char *)p++) == '\t' && !O_ISSET(sp, O_LIST))
			scno += TAB_OFF(sp, scno);
		else
			scno += cname[ch].len;

		if (cnop != NULL) {
			if (cno_cnt == 0)
				break;
			--cno_cnt;
		}
	}

	return (scno);
}