Esempio n. 1
0
int
ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
    uint8_t mlen, uint32_t value)
{
	struct radix_node_head *rnh;
	struct table_entry *ent;
	struct radix_node *rn;

	if (tbl >= IPFW_TABLES_MAX)
		return (EINVAL);
	rnh = ch->tables[tbl];
	ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO);
	if (ent == NULL)
		return (ENOMEM);
	ent->value = value;
	KEY_LEN(ent->addr) = KEY_LEN(ent->mask) = 8;
	ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
	ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
	IPFW_WLOCK(ch);
	rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent);
	if (rn == NULL) {
		IPFW_WUNLOCK(ch);
		free(ent, M_IPFW_TBL);
		return (EEXIST);
	}
	IPFW_WUNLOCK(ch);
	return (0);
}
Esempio n. 2
0
/*! \brief Check for equality. */
static bool hhelem_isequal(hhelem_t *elm, const char *key, uint16_t len)
{
	uint16_t klen;
	memcpy(&klen, KEY_LEN(elm->d), sizeof(klen));
	if (klen != len) {
		return false;
	}

	return memcmp(KEY_STR(elm->d), key, len) == 0;
}
Esempio n. 3
0
int
ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
    uint32_t *val, int type)
{
	struct radix_node_head *rnh;
	struct table_xentry *xent;
	struct sockaddr_in6 sa6;
	struct xaddr_iface iface;

	if (tbl >= V_fw_tables_max)
		return (0);
	if ((rnh = ch->xtables[tbl]) == NULL)
		return (0);

	switch (type) {
	case IPFW_TABLE_CIDR:
		KEY_LEN(sa6) = KEY_LEN_INET6;
		memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr));
		xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
		break;

	case IPFW_TABLE_INTERFACE:
		KEY_LEN(iface) = KEY_LEN_IFACE +
		    strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1;
		/* Assume direct match */
		/* FIXME: Add interface pattern matching */
		xent = (struct table_xentry *)(rnh->rnh_matchaddr(&iface, rnh));
		break;

	default:
		return (0);
	}

	if (xent != NULL) {
		*val = xent->value;
		return (1);
	}
	return (0);
}
Esempio n. 4
0
int
ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
    uint8_t mlen)
{
	struct radix_node_head *rnh;
	struct table_entry *ent;
	struct sockaddr_in sa, mask;

	if (tbl >= IPFW_TABLES_MAX)
		return (EINVAL);
	rnh = ch->tables[tbl];
	KEY_LEN(sa) = KEY_LEN(mask) = 8;
	mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
	sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
	IPFW_WLOCK(ch);
	ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh);
	if (ent == NULL) {
		IPFW_WUNLOCK(ch);
		return (ESRCH);
	}
	IPFW_WUNLOCK(ch);
	free(ent, M_IPFW_TBL);
	return (0);
}
/*
 * ex_prchars --
 *	Local routine to dump characters to the screen.
 */
static int
ex_prchars(SCR *sp, const CHAR_T *p, size_t *colp, size_t len, 
	    u_int flags, int repeatc)
{
	CHAR_T ch;
	const char *kp;
	size_t col, tlen, ts;

	if (O_ISSET(sp, O_LIST))
		LF_SET(E_C_LIST);
	ts = O_VAL(sp, O_TABSTOP);
	for (col = *colp; len--;)
		if ((ch = *p++) == L('\t') && !LF_ISSET(E_C_LIST))
			for (tlen = ts - col % ts;
			    col < sp->cols && tlen--; ++col) {
				(void)ex_printf(sp,
				    "%c", repeatc ? repeatc : ' ');
				if (INTERRUPTED(sp))
					goto intr;
			}
		else {
			/* XXXX */
			if (INTISWIDE(ch)) {
			    CHAR_T str[2] = {0, 0};
			    str[0] = ch;
			    INT2CHAR(sp, str, 2, kp, tlen);
			} else {
			    kp = (char *)KEY_NAME(sp, ch);
			    tlen = KEY_LEN(sp, ch);
			}
			if (!repeatc  && col + tlen < sp->cols) {
				(void)ex_puts(sp, kp);
				col += tlen;
			} else
				for (; tlen--; ++kp, ++col) {
					if (col == sp->cols) {
						col = 0;
						(void)ex_puts(sp, "\n");
					}
					(void)ex_printf(sp,
					    "%c", repeatc ? repeatc : *kp);
					if (INTERRUPTED(sp))
						goto intr;
				}
		}
intr:	*colp = col;
	return (0);
}
Esempio n. 6
0
int
ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
    uint32_t *val)
{
	struct radix_node_head *rnh;
	struct table_entry *ent;
	struct sockaddr_in sa;

	if (tbl >= IPFW_TABLES_MAX)
		return (0);
	rnh = ch->tables[tbl];
	KEY_LEN(sa) = 8;
	sa.sin_addr.s_addr = addr;
	ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh));
	if (ent != NULL) {
		*val = ent->value;
		return (1);
	}
	return (0);
}
Esempio n. 7
0
int
ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
    uint32_t *val)
{
	struct radix_node_head *rnh;
	struct table_entry *ent;
	struct sockaddr_in sa;

	if (tbl >= V_fw_tables_max)
		return (0);
	if ((rnh = ch->tables[tbl]) == NULL)
		return (0);
	KEY_LEN(sa) = KEY_LEN_INET;
	sa.sin_addr.s_addr = addr;
	ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh));
	if (ent != NULL) {
		*val = ent->value;
		return (1);
	}
	return (0);
}
Esempio n. 8
0
File: msg.c Progetto: 2asoft/freebsd
/*
 * msgq_status --
 *	Report on the file's status.
 *
 * PUBLIC: void msgq_status(SCR *, recno_t, u_int);
 */
void
msgq_status(
	SCR *sp,
	recno_t lno,
	u_int flags)
{
	recno_t last;
	size_t blen, len;
	int cnt, needsep;
	const char *t;
	char **ap, *bp, *np, *p, *s, *ep;
	CHAR_T *wp;
	size_t wlen;

	/* Get sufficient memory. */
	len = strlen(sp->frp->name);
	GET_SPACE_GOTOC(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128);
	p = bp;
	ep = bp + blen;

	/* Convert the filename. */
	CHAR2INT(sp, sp->frp->name, len + 1, wp, wlen);

	/* Copy in the filename. */
	for (; *wp != '\0'; ++wp) {
		len = KEY_LEN(sp, *wp);
		memcpy(p, KEY_NAME(sp, *wp), len);
		p += len;
	}
	np = p;
	*p++ = ':';
	*p++ = ' ';

	/* Copy in the argument count. */
	if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) {
		for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt);
		if (cnt > 1) {
			(void)snprintf(p, ep - p,
			    msg_cat(sp, "317|%d files to edit", NULL), cnt);
			p += strlen(p);
			*p++ = ':';
			*p++ = ' ';
		}
		F_CLR(sp, SC_STATUS_CNT);
	}

	/*
	 * See nvi/exf.c:file_init() for a description of how and when the
	 * read-only bit is set.
	 *
	 * !!!
	 * The historic display for "name changed" was "[Not edited]".
	 */
	needsep = 0;
	if (F_ISSET(sp->frp, FR_NEWFILE)) {
		F_CLR(sp->frp, FR_NEWFILE);
		t = msg_cat(sp, "021|new file", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	} else {
		if (F_ISSET(sp->frp, FR_NAMECHANGE)) {
			t = msg_cat(sp, "022|name changed", &len);
			memcpy(p, t, len);
			p += len;
			needsep = 1;
		}
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		if (F_ISSET(sp->ep, F_MODIFIED))
			t = msg_cat(sp, "023|modified", &len);
		else
			t = msg_cat(sp, "024|unmodified", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	}
	if (F_ISSET(sp->frp, FR_UNLOCKED)) {
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		t = msg_cat(sp, "025|UNLOCKED", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	}
	if (O_ISSET(sp, O_READONLY)) {
		if (needsep) {
			*p++ = ',';
			*p++ = ' ';
		}
		t = msg_cat(sp, "026|readonly", &len);
		memcpy(p, t, len);
		p += len;
		needsep = 1;
	}
	if (needsep) {
		*p++ = ':';
		*p++ = ' ';
	}
	if (LF_ISSET(MSTAT_SHOWLAST)) {
		if (db_last(sp, &last))
			return;
		if (last == 0) {
			t = msg_cat(sp, "028|empty file", &len);
			memcpy(p, t, len);
			p += len;
		} else {
			t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len);
			(void)snprintf(p, ep - p, t, (u_long)lno, (u_long)last,
			    ((u_long)lno * 100) / last);
			p += strlen(p);
		}
	} else {
		t = msg_cat(sp, "029|line %lu", &len);
		(void)snprintf(p, ep - p, t, (u_long)lno);
		p += strlen(p);
	}
#ifdef DEBUG
	(void)snprintf(p, ep - p, " (pid %lu)", (u_long)getpid());
	p += strlen(p);
#endif
	*p++ = '\n';
	len = p - bp;

	/*
	 * There's a nasty problem with long path names.  Cscope and tags files
	 * can result in long paths and vi will request a continuation key from
	 * the user as soon as it starts the screen.  Unfortunately, the user
	 * has already typed ahead, and chaos results.  If we assume that the
	 * characters in the filenames and informational messages only take a
	 * single screen column each, we can trim the filename.
	 *
	 * XXX
	 * Status lines get put up at fairly awkward times.  For example, when
	 * you do a filter read (e.g., :read ! echo foo) in the top screen of a
	 * split screen, we have to repaint the status lines for all the screens
	 * below the top screen.  We don't want users having to enter continue
	 * characters for those screens.  Make it really hard to screw this up.
	 */
	s = bp;
	if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) {
		for (; s < np && (*s != '/' || (p - s) > sp->cols - 3); ++s);
		if (s == np) {
			s = p - (sp->cols - 5);
			*--s = ' ';
		}
		*--s = '.';
		*--s = '.';
		*--s = '.';
		len = p - s;
	}

	/* Flush any waiting ex messages. */
	(void)ex_fflush(sp);

	sp->gp->scr_msg(sp, M_INFO, s, len);

	FREE_SPACE(sp, bp, blen);
alloc_err:
	return;
}
Esempio n. 9
0
int
ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
    uint8_t plen, uint8_t mlen, uint8_t type)
{
	struct radix_node_head *rnh, **rnh_ptr;
	struct table_entry *ent;
	in_addr_t addr;
	struct sockaddr_in sa, mask;
	struct sockaddr *sa_ptr, *mask_ptr;
	char c;

	if (tbl >= V_fw_tables_max)
		return (EINVAL);

	switch (type) {
	case IPFW_TABLE_CIDR:
		if (plen == sizeof(in_addr_t)) {
			/* Set 'total' structure length */
			KEY_LEN(sa) = KEY_LEN_INET;
			KEY_LEN(mask) = KEY_LEN_INET;
			mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
			addr = *((in_addr_t *)paddr);
			sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
			rnh_ptr = &ch->tables[tbl];
			sa_ptr = (struct sockaddr *)&sa;
			mask_ptr = (struct sockaddr *)&mask;
#ifdef INET6
		} else if (plen == sizeof(struct in6_addr)) {
			/* IPv6 case */
			if (mlen > 128)
				return (EINVAL);
			struct sockaddr_in6 sa6, mask6;
			memset(&sa6, 0, sizeof(struct sockaddr_in6));
			memset(&mask6, 0, sizeof(struct sockaddr_in6));
			/* Set 'total' structure length */
			KEY_LEN(sa6) = KEY_LEN_INET6;
			KEY_LEN(mask6) = KEY_LEN_INET6;
			ipv6_writemask(&mask6.sin6_addr, mlen);
			memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr));
			APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr);
			rnh_ptr = &ch->xtables[tbl];
			sa_ptr = (struct sockaddr *)&sa6;
			mask_ptr = (struct sockaddr *)&mask6;
#endif
		} else {
			/* Unknown CIDR type */
			return (EINVAL);
		}
		break;

	case IPFW_TABLE_INTERFACE:
		/* Check if string is terminated */
		c = ((char *)paddr)[IF_NAMESIZE - 1];
		((char *)paddr)[IF_NAMESIZE - 1] = '\0';
		if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0'))
			return (EINVAL);

		struct xaddr_iface ifname, ifmask;
		memset(&ifname, 0, sizeof(ifname));

		/* Include last \0 into comparison */
		mlen++;

		/* Set 'total' structure length */
		KEY_LEN(ifname) = KEY_LEN_IFACE + mlen;
		KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen;
		/* Assume direct match */
		/* FIXME: Add interface pattern matching */
#if 0
		memset(ifmask.ifname, 0xFF, IF_NAMESIZE);
		mask_ptr = (struct sockaddr *)&ifmask;
#endif
		mask_ptr = NULL;
		memcpy(ifname.ifname, paddr, mlen);
		/* Set pointers */
		rnh_ptr = &ch->xtables[tbl];
		sa_ptr = (struct sockaddr *)&ifname;

		break;

	default:
		return (EINVAL);
	}

	IPFW_WLOCK(ch);
	if ((rnh = *rnh_ptr) == NULL) {
		IPFW_WUNLOCK(ch);
		return (ESRCH);
	}

	if (ch->tabletype[tbl] != type) {
		IPFW_WUNLOCK(ch);
		return (EINVAL);
	}

	ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh);
	IPFW_WUNLOCK(ch);

	if (ent == NULL)
		return (ESRCH);

	free(ent, M_IPFW_TBL);
	return (0);
}
Esempio n. 10
0
int
ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
    uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value)
{
	struct radix_node_head *rnh, **rnh_ptr;
	struct table_entry *ent;
	struct table_xentry *xent;
	struct radix_node *rn;
	in_addr_t addr;
	int offset;
	void *ent_ptr;
	struct sockaddr *addr_ptr, *mask_ptr;
	char c;

	if (tbl >= V_fw_tables_max)
		return (EINVAL);

	switch (type) {
	case IPFW_TABLE_CIDR:
		if (plen == sizeof(in_addr_t)) {
#ifdef INET
			/* IPv4 case */
			if (mlen > 32)
				return (EINVAL);
			ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
			ent->value = value;
			/* Set 'total' structure length */
			KEY_LEN(ent->addr) = KEY_LEN_INET;
			KEY_LEN(ent->mask) = KEY_LEN_INET;
			/* Set offset of IPv4 address in bits */
			offset = OFF_LEN_INET;
			ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
			addr = *((in_addr_t *)paddr);
			ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
			/* Set pointers */
			rnh_ptr = &ch->tables[tbl];
			ent_ptr = ent;
			addr_ptr = (struct sockaddr *)&ent->addr;
			mask_ptr = (struct sockaddr *)&ent->mask;
#endif
#ifdef INET6
		} else if (plen == sizeof(struct in6_addr)) {
			/* IPv6 case */
			if (mlen > 128)
				return (EINVAL);
			xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
			xent->value = value;
			/* Set 'total' structure length */
			KEY_LEN(xent->a.addr6) = KEY_LEN_INET6;
			KEY_LEN(xent->m.mask6) = KEY_LEN_INET6;
			/* Set offset of IPv6 address in bits */
			offset = OFF_LEN_INET6;
			ipv6_writemask(&xent->m.mask6.sin6_addr, mlen);
			memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr));
			APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr);
			/* Set pointers */
			rnh_ptr = &ch->xtables[tbl];
			ent_ptr = xent;
			addr_ptr = (struct sockaddr *)&xent->a.addr6;
			mask_ptr = (struct sockaddr *)&xent->m.mask6;
#endif
		} else {
			/* Unknown CIDR type */
			return (EINVAL);
		}
		break;
	
	case IPFW_TABLE_INTERFACE:
		/* Check if string is terminated */
		c = ((char *)paddr)[IF_NAMESIZE - 1];
		((char *)paddr)[IF_NAMESIZE - 1] = '\0';
		if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0'))
			return (EINVAL);

		/* Include last \0 into comparison */
		mlen++;

		xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
		xent->value = value;
		/* Set 'total' structure length */
		KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen;
		KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen;
		/* Set offset of interface name in bits */
		offset = OFF_LEN_IFACE;
		memcpy(xent->a.iface.ifname, paddr, mlen);
		/* Assume direct match */
		/* TODO: Add interface pattern matching */
#if 0
		memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE);
		mask_ptr = (struct sockaddr *)&xent->m.ifmask;
#endif
		/* Set pointers */
		rnh_ptr = &ch->xtables[tbl];
		ent_ptr = xent;
		addr_ptr = (struct sockaddr *)&xent->a.iface;
		mask_ptr = NULL;
		break;

	default:
		return (EINVAL);
	}

	IPFW_WLOCK(ch);

	/* Check if tabletype is valid */
	if ((ch->tabletype[tbl] != 0) && (ch->tabletype[tbl] != type)) {
		IPFW_WUNLOCK(ch);
		free(ent_ptr, M_IPFW_TBL);
		return (EINVAL);
	}

	/* Check if radix tree exists */
	if ((rnh = *rnh_ptr) == NULL) {
		IPFW_WUNLOCK(ch);
		/* Create radix for a new table */
		if (!rn_inithead((void **)&rnh, offset)) {
			free(ent_ptr, M_IPFW_TBL);
			return (ENOMEM);
		}

		IPFW_WLOCK(ch);
		if (*rnh_ptr != NULL) {
			/* Tree is already attached by other thread */
			rn_detachhead((void **)&rnh);
			rnh = *rnh_ptr;
			/* Check table type another time */
			if (ch->tabletype[tbl] != type) {
				IPFW_WUNLOCK(ch);
				free(ent_ptr, M_IPFW_TBL);
				return (EINVAL);
			}
		} else {
			*rnh_ptr = rnh;
			/* 
			 * Set table type. It can be set already
			 * (if we have IPv6-only table) but setting
			 * it another time does not hurt
			 */
			ch->tabletype[tbl] = type;
		}
	}

	rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr);
	IPFW_WUNLOCK(ch);

	if (rn == NULL) {
		free(ent_ptr, M_IPFW_TBL);
		return (EEXIST);
	}
	return (0);
}
Esempio n. 11
0
/*! \brief Helper function to read key length. */
static inline uint16_t key_readlen(const void *k)
{
	uint16_t ret;
	memcpy(&ret, KEY_LEN(k), sizeof(ret));
	return ret;
}
Esempio n. 12
0
value_t *hhash_map(hhash_t* tbl, const char* key, uint16_t len, uint16_t mode)
{
	if (tbl == NULL) {
		return NULL;
	}

	/* Find an exact match in <id, id + HOP_LEN). */
	uint32_t id = hash(key, len) % tbl->size;
	int dist = find_match(tbl, id, key, len);
	if (dist <= HOP_LEN) {
		/* Found exact match, return value. */
		hhelem_t *match = &tbl->item[(id + dist) % tbl->size];
		return (value_t *)KEY_VAL(match->d);
	}

	/* We didn't find an exact match, continue only if inserting. */
	if (!(mode & HHASH_INSERT)) {
		return NULL;
	} else if (tbl->weight >= tbl->size) { /* Or full table. */
		return NULL;
	}

	/* Reduce distance to fit <id, id + HOP_LEN) */
	dist = find_free(tbl, id);
	if (dist < 0) { /* Did not find any fit. */
		return NULL;
	}
	int empty = (id + dist) % tbl->size;
	while (dist >= HOP_LEN) {
		dist = reduce_dist(tbl, dist, &empty);
		/* Couldn't reduce the distance, no fit available. */
		if (dist < 0) {
			return NULL;
		}
	}

	/* Insert to given position. */
	char *new_key = tbl->mm.alloc(tbl->mm.ctx, HHKEY_LEN + len);
	if (new_key != NULL) {
		memset(KEY_VAL(new_key), 0,    sizeof(value_t));
		memcpy(KEY_LEN(new_key), &len, sizeof(uint16_t));
		memcpy(KEY_STR(new_key), key,  len);
	} else {
		return NULL;
	}

	/* found free elm 'k' which is in <id, id + HOP_LEN) */
	assert(tbl->item[empty].d == NULL);
	tbl->item[id].hop |= HOP_BIT(dist);
	tbl->item[empty].d = new_key;

	++tbl->weight;

	/* Free old index. */
	if (tbl->index) {
		if (tbl->mm.free) {
			free(tbl->index);
		}
		tbl->index = NULL;
	}

	return (value_t *)KEY_VAL(new_key);
}
Esempio n. 13
0
/*
 * vs_output --
 *	Output the text to the screen.
 */
static void
vs_output(SCR *sp, mtype_t mtype, const char *line, int llen)
{
	unsigned char *kp;
	GS *gp;
	VI_PRIVATE *vip;
	size_t chlen, notused;
	int ch, len, rlen, tlen;
	const char *p, *t;
	char *cbp, *ecbp, cbuf[128];

	gp = sp->gp;
	vip = VIP(sp);
	for (p = line, rlen = llen; llen > 0;) {
		/* Get the next physical 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 + vip->lcontinue > sp->cols)
			len = sp->cols - vip->lcontinue;

		/*
		 * If the first line output, do nothing.  If the second line
		 * output, draw the divider line.  If drew a full screen, we
		 * remove the divider line.  If it's a continuation line, move
		 * to the continuation point, else, move the screen up.
		 */
		if (vip->lcontinue == 0) {
			if (!IS_ONELINE(sp)) {
				if (vip->totalcount == 1) {
					(void)gp->scr_move(sp,
					    LASTLINE(sp) - 1, 0);
					(void)gp->scr_clrtoeol(sp);
					(void)vs_divider(sp);
					F_SET(vip, VIP_DIVIDER);
					++vip->totalcount;
					++vip->linecount;
				}
				if (vip->totalcount == sp->t_maxrows &&
				    F_ISSET(vip, VIP_DIVIDER)) {
					--vip->totalcount;
					--vip->linecount;
					F_CLR(vip, VIP_DIVIDER);
				}
			}
			if (vip->totalcount != 0)
				vs_scroll(sp, NULL, SCROLL_W_QUIT);

			(void)gp->scr_move(sp, LASTLINE(sp), 0);
			++vip->totalcount;
			++vip->linecount;

			if (INTERRUPTED(sp))
				break;
		} else
			(void)gp->scr_move(sp, LASTLINE(sp), vip->lcontinue);

		/* Error messages are in inverse video. */
		if (mtype == M_ERR)
			(void)gp->scr_attr(sp, SA_INVERSE, 1);

		/* Display the line, doing character translation. */
#define	FLUSH {								\
	*cbp = '\0';							\
	(void)gp->scr_addstr(sp, cbuf, cbp - cbuf);			\
	cbp = cbuf;							\
}
		ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
		for (t = line, tlen = len; tlen--; ++t) {
			ch = *t;
			/*
			 * Replace tabs with spaces, there are places in
			 * ex that do column calculations without looking
			 * at <tabs> -- and all routines that care about
			 * <tabs> do their own expansions.  This catches
			 * <tabs> in things like tag search strings.
			 */
			if (ch == '\t')
				ch = ' ';
			chlen = KEY_LEN(sp, ch);
			if (cbp + chlen >= ecbp)
				FLUSH;
			for (kp = KEY_NAME(sp, ch); chlen--;)
				*cbp++ = *kp++;
		}
		if (cbp > cbuf)
			FLUSH;
		if (mtype == M_ERR)
			(void)gp->scr_attr(sp, SA_INVERSE, 0);

		/* Clear the rest of the line. */
		(void)gp->scr_clrtoeol(sp);

		/* If we loop, it's a new line. */
		vip->lcontinue = 0;

		/* Reset for the next line. */
		line += len;
		llen -= len;
		if (p != NULL) {
			++line;
			--llen;
		}
	}

	/* Set up next continuation line. */
	if (p == NULL)
		gp->scr_cursor(sp, &notused, &vip->lcontinue);
}
/*
 * vs_line --
 *	Update one line on the screen.
 *
 * PUBLIC: int vs_line __P((SCR *, SMAP *, size_t *, size_t *));
 */
int
vs_line(SCR *sp, SMAP *smp, size_t *yp, size_t *xp)
{
	unsigned char *kp;
	GS *gp;
	SMAP *tsmp;
	size_t chlen = 0, cno_cnt, cols_per_screen, len, nlen;
	size_t offset_in_char, offset_in_line, oldx, oldy;
	size_t scno, skip_cols, skip_screens;
	int dne, is_cached, is_partial, is_tab, no_draw;
	int list_tab, list_dollar;
	CHAR_T *p;
	CHAR_T *cbp, *ecbp, cbuf[128];
	ARG_CHAR_T ch = L('\0');

#if defined(DEBUG) && 0
	vtrace(sp, "vs_line: row %u: line: %u off: %u\n",
	    smp - HMAP, smp->lno, smp->off);
#endif
	/*
	 * If ex modifies the screen after ex output is already on the screen,
	 * don't touch it -- we'll get scrolling wrong, at best.
	 */
	no_draw = 0;
	if (!F_ISSET(sp, SC_TINPUT_INFO) && VIP(sp)->totalcount > 1)
		no_draw = 1;
	if (F_ISSET(sp, SC_SCR_EXWROTE) && (size_t)(smp - HMAP) != LASTLINE(sp))
		no_draw = 1;

	/*
	 * Assume that, if the cache entry for the line is filled in, the
	 * line is already on the screen, and all we need to do is return
	 * the cursor position.  If the calling routine doesn't need the
	 * cursor position, we can just return.
	 */
	is_cached = SMAP_CACHE(smp);
	if (yp == NULL && (is_cached || no_draw))
		return (0);

	/*
	 * A nasty side effect of this routine 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.
	 *
	 * Move to the line.  This routine can be called by vs_sm_position(),
	 * which uses it to fill in the cache entry so it can figure out what
	 * the real contents of the screen are.  Because of this, we have to
	 * return to whereever we started from.
	 */
	gp = sp->gp;
	(void)gp->scr_cursor(sp, &oldy, &oldx);
	(void)gp->scr_move(sp, smp - HMAP, 0);

	/* Get the line. */
	dne = db_get(sp, smp->lno, 0, &p, &len);

	/*
	 * Special case if we're printing the info/mode line.  Skip printing
	 * the leading number, as well as other minor setup.  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 columns for this screen.
	 * Set the number of chars or screens to skip until a character is to
	 * be displayed.
	 */
	cols_per_screen = sp->cols;
	if (O_ISSET(sp, O_LEFTRIGHT)) {
		skip_screens = 0;
		skip_cols = smp->coff;
	} else {
		skip_screens = smp->soff - 1;
		skip_cols = skip_screens * cols_per_screen;
	}

	list_tab = O_ISSET(sp, O_LIST);
	if (F_ISSET(sp, SC_TINPUT_INFO))
		list_dollar = 0;
	else {
		list_dollar = list_tab;

		/*
		 * If O_NUMBER is set, the line doesn't exist and it's line
		 * number 1, i.e., an empty file, display the line number.
		 *
		 * If O_NUMBER is set, the line exists and the first character
		 * on the screen is the first character in the line, display
		 * the line number.
		 *
		 * !!!
		 * If O_NUMBER set, decrement the number of columns in the
		 * first screen.  DO NOT CHANGE THIS -- IT'S RIGHT!  The
		 * rest of the code expects this to reflect the number of
		 * columns in the first screen, regardless of the number of
		 * columns we're going to skip.
		 */
		if (O_ISSET(sp, O_NUMBER)) {
			cols_per_screen -= O_NUMBER_LENGTH;
			if ((!dne || smp->lno == 1) && skip_cols == 0) {
				nlen = snprintf((char*)cbuf,
				    sizeof(cbuf), O_NUMBER_FMT,
				    (unsigned long)smp->lno);
				(void)gp->scr_addstr(sp, (char*)cbuf, nlen);
			}
		}
	}

	/*
	 * Special case non-existent lines and the first line of an empty
	 * file.  In both cases, the cursor position is 0, but corrected
	 * as necessary for the O_NUMBER field, if it was displayed.
	 */
	if (dne || len == 0) {
		/* Fill in the cursor. */
		if (yp != NULL && smp->lno == sp->lno) {
			*yp = smp - HMAP;
			*xp = sp->cols - cols_per_screen;
		}

		/* If the line is on the screen, quit. */
		if (is_cached || no_draw)
			goto ret1;

		/* Set line cache information. */
		smp->c_sboff = smp->c_eboff = 0;
		smp->c_scoff = smp->c_eclen = 0;

		/*
		 * Lots of special cases for empty lines, but they only apply
		 * if we're displaying the first screen of the line.
		 */
		if (skip_cols == 0) {
			if (dne) {
				if (smp->lno == 1) {
					if (list_dollar) {
						ch = L('$');
						goto empty;
					}
				} else {
					ch = L('~');
					goto empty;
				}
			} else
				if (list_dollar) {
					ch = L('$');
empty:					(void)gp->scr_addstr(sp,
					    (const char *)KEY_NAME(sp, ch),
					    KEY_LEN(sp, ch));
				}
		}

		(void)gp->scr_clrtoeol(sp);
		(void)gp->scr_move(sp, oldy, oldx);
		return (0);
	}

	/* If we shortened this line in another screen, the cursor
	 * position may have fallen off.
	 */
	if (sp->lno == smp->lno && sp->cno >= len)
	    sp->cno = len - 1;

	/*
	 * If we just wrote this or a previous line, we cached the starting
	 * and ending positions of that line.  The way it works is we keep
	 * information about the lines displayed in the SMAP.  If we're
	 * painting the screen in the forward direction, this saves us from
	 * reformatting the physical line for every line on the screen.  This
	 * wins big on binary files with 10K lines.
	 *
	 * Test for the first screen of the line, then the current screen line,
	 * then the line behind us, then do the hard work.  Note, it doesn't
	 * do us any good to have a line in front of us -- it would be really
	 * hard to try and figure out tabs in the reverse direction, i.e. how
	 * many spaces a tab takes up in the reverse direction depends on
	 * what characters preceded it.
	 *
	 * Test for the first screen of the line.
	 */
	if (skip_cols == 0) {
		smp->c_sboff = offset_in_line = 0;
		smp->c_scoff = offset_in_char = 0;
		p = &p[offset_in_line];
		goto display;
	}

	/* Test to see if we've seen this exact line before. */
	if (is_cached) {
		offset_in_line = smp->c_sboff;
		offset_in_char = smp->c_scoff;
		p = &p[offset_in_line];

		/* Set cols_per_screen to 2nd and later line length. */
		if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen)
			cols_per_screen = sp->cols;
		goto display;
	}

	/* Test to see if we saw an earlier part of this line before. */
	if (smp != HMAP &&
	    SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) {
		if (tsmp->c_eclen != tsmp->c_ecsize) {
			offset_in_line = tsmp->c_eboff;
			offset_in_char = tsmp->c_eclen;
		} else {
			offset_in_line = tsmp->c_eboff + 1;
			offset_in_char = 0;
		}

		/* Put starting info for this line in the cache. */
		smp->c_sboff = offset_in_line;
		smp->c_scoff = offset_in_char;
		p = &p[offset_in_line];

		/* Set cols_per_screen to 2nd and later line length. */
		if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen)
			cols_per_screen = sp->cols;
		goto display;
	}

	scno = 0;
	offset_in_line = 0;
	offset_in_char = 0;

	/* Do it the hard way, for leftright scrolling screens. */
	if (O_ISSET(sp, O_LEFTRIGHT)) {
		for (; offset_in_line < len; ++offset_in_line) {
			chlen = (ch = (UCHAR_T)*p++) == L('\t') && !list_tab ?
			    TAB_OFF(scno) : KEY_COL(sp, ch);
			if ((scno += chlen) >= skip_cols)
				break;
		}

		/* Set cols_per_screen to 2nd and later line length. */
		cols_per_screen = sp->cols;

		/* Put starting info for this line in the cache. */
		if (offset_in_line >= len) {
			smp->c_sboff = offset_in_line;
			smp->c_scoff = 255;
		} else if (scno != skip_cols) {
			smp->c_sboff = offset_in_line;
			smp->c_scoff =
			    offset_in_char = chlen - (scno - skip_cols);
			--p;
		} else {
			smp->c_sboff = ++offset_in_line;
			smp->c_scoff = 0;
		}
	}

	/* Do it the hard way, for historic line-folding screens. */
	else {
		for (; offset_in_line < len; ++offset_in_line) {
			chlen = (ch = (UCHAR_T)*p++) == L('\t') && !list_tab ?
			    TAB_OFF(scno) : KEY_COL(sp, ch);
			if ((scno += chlen) < cols_per_screen)
				continue;
			scno -= cols_per_screen;

			/* Set cols_per_screen to 2nd and later line length. */
			cols_per_screen = sp->cols;

			/*
			 * If crossed the last skipped screen boundary, start
			 * displaying the characters.
			 */
			if (--skip_screens == 0)
				break;
		}

		/* Put starting info for this line in the cache. */
		if (scno != 0) {
			smp->c_sboff = offset_in_line;
			smp->c_scoff = offset_in_char = chlen - scno;
			--p;
		} else {
			smp->c_sboff = ++offset_in_line;
			smp->c_scoff = 0;
		}
	}

display:
	/*
	 * Set the number of characters to skip before reaching the cursor
	 * character.  Offset by 1 and use 0 as a flag value.  Vs_line is
	 * called repeatedly with a valid pointer to a cursor position.
	 * Don't fill anything in unless it's the right line and the right
	 * character, and the right part of the character...
	 */
	if (yp == NULL ||
	    smp->lno != sp->lno || sp->cno < offset_in_line ||
	    offset_in_line + cols_per_screen < sp->cno) {
		cno_cnt = 0;
		/* If the line is on the screen, quit. */
		if (is_cached || no_draw)
			goto ret1;
	} else
		cno_cnt = (sp->cno - offset_in_line) + 1;

	/* This is the loop that actually displays characters. */
	ecbp = (cbp = cbuf) + sizeof(cbuf)/sizeof(CHAR_T) - 1;
	for (is_partial = 0, scno = 0;
	    offset_in_line < len; ++offset_in_line, offset_in_char = 0) {
		if ((ch = (UCHAR_T)*p++) == L('\t') && !list_tab) {
			scno += chlen = TAB_OFF(scno) - offset_in_char;
			is_tab = 1;
		} else {
			scno += chlen = KEY_COL(sp, ch) - offset_in_char;
			is_tab = 0;
		}

		/*
		 * Only display up to the right-hand column.  Set a flag if
		 * the entire character wasn't displayed for use in setting
		 * the cursor.  If reached the end of the line, set the cache
		 * info for the screen.  Don't worry about there not being
		 * characters to display on the next screen, its lno/off won't
		 * match up in that case.
		 */
		if (scno >= cols_per_screen) {
			if (is_tab == 1) {
				chlen -= scno - cols_per_screen;
				smp->c_ecsize = smp->c_eclen = chlen;
				scno = cols_per_screen;
			} else {
				smp->c_ecsize = chlen;
				chlen -= scno - cols_per_screen;
				smp->c_eclen = chlen;

				if (scno > cols_per_screen)
					is_partial = 1;
			}
			smp->c_eboff = offset_in_line;

			/* Terminate the loop. */
			offset_in_line = len;
		}

		/*
		 * 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 character -- if it's normal display
		 * mode, it goes on the last column of the character.  If
		 * it's input mode, it goes on the first.  In normal mode,
		 * set the cursor only if the entire character was displayed.
		 */
		if (cno_cnt &&
		    --cno_cnt == 0 && (F_ISSET(sp, SC_TINPUT) || !is_partial)) {
			*yp = smp - HMAP;
			if (F_ISSET(sp, SC_TINPUT))
				if (is_partial)
					*xp = scno - smp->c_ecsize;
				else
					*xp = scno - chlen;
			else
				*xp = scno - 1;
			if (O_ISSET(sp, O_NUMBER) &&
			    !F_ISSET(sp, SC_TINPUT_INFO) && skip_cols == 0)
				*xp += O_NUMBER_LENGTH;

			/* If the line is on the screen, quit. */
			if (is_cached || no_draw)
				goto ret1;
		}

		/* If the line is on the screen, don't display anything. */
		if (is_cached || no_draw)
			continue;

#define	FLUSH {								\
	*cbp = '\0';							\
	(void)gp->scr_waddstr(sp, cbuf, cbp - cbuf);			\
	cbp = cbuf;							\
}
		/*
		 * Display the character.  We do tab expansion here because
		 * the screen interface doesn't have any way to set the tab
		 * length.  Note, it's theoretically possible for chlen to
		 * be larger than cbuf, if the user set a impossibly large
		 * tabstop.
		 */
		if (is_tab)
			while (chlen--) {
				if (cbp >= ecbp)
					FLUSH;
				*cbp++ = TABCH;
			}
		else {
			if (cbp + chlen >= ecbp)
				FLUSH;

			/* don't display half a wide character */
			if (is_partial && CHAR_WIDTH(sp, ch) > 1) {
				*cbp++ = ' ';
				break;
			}

			/* XXXX this needs some rethinking */
			if (INTISWIDE(ch)) {
				/* Put a space before non-spacing char. */
				if (CHAR_WIDTH(sp, ch) <= 0)
					*cbp++ = L(' ');
				*cbp++ = ch;
			} else
				for (kp = KEY_NAME(sp, ch) + offset_in_char; 
				     chlen--;)
					*cbp++ = (u_char)*kp++;
		}
	}

	if (scno < cols_per_screen) {
		/* If didn't paint the whole line, update the cache. */
		smp->c_ecsize = smp->c_eclen = KEY_LEN(sp, ch);
		smp->c_eboff = len - 1;

		/*
		 * 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 (list_dollar) {
			++scno;

			chlen = KEY_LEN(sp, L('$'));
			if (cbp + chlen >= ecbp)
				FLUSH;
			for (kp = KEY_NAME(sp, L('$')); chlen--;)
				*cbp++ = *kp++;
		}

		/* If still didn't paint the whole line, clear the rest. */
		if (scno < cols_per_screen)
			(void)gp->scr_clrtoeol(sp);
	}

	/* Flush any buffered characters. */
	if (cbp > cbuf)
		FLUSH;

ret1:	(void)gp->scr_move(sp, oldy, oldx);
	return (0);
}
Esempio n. 15
0
File: hhash.c Progetto: idtek/knot
int main(int argc, char *argv[])
{
	plan(11);

	/* Create memory pool context. */
	struct mempool *pool = mp_new(64 * 1024);
	knot_mm_t mm;
	mm.ctx = pool;
	mm.alloc = (knot_mm_alloc_t)mp_alloc;
	mm.free = NULL;

	/* Create hashtable */
	int ret = KNOT_EOK;
	uint16_t len = 0;
	const char *key = "mykey", *cur = NULL, *prev = NULL;
	value_t val = (void*)0xdeadbeef, *rval = NULL;
	hhash_iter_t it;
	hhash_t *tbl = hhash_create_mm(ELEM_COUNT, &mm);
	ok(tbl != NULL, "hhash: create");
	if (tbl == NULL) {
		return KNOT_ERROR; /* No point in testing further on. */
	}

	/* Generate random keys. */
	char *keys[ELEM_COUNT];
	unsigned nfilled = 0;
	for (unsigned i = 0; i < ELEM_COUNT; ++i) {
		keys[i] = test_randstr_mm(&mm);
	}

	/* Insert single element. */
	ret = hhash_insert(tbl, key, KEY_LEN(key), val);
	ok(ret == KNOT_EOK, "hhash: insert single element");

	/* Retrieve nonexistent element. */
	cur = "nokey";
	rval = hhash_find(tbl, cur, KEY_LEN(cur));
	ok(rval == NULL, "hhash: find non-existent element");

	/* Retrieve single element. */
	rval = hhash_find(tbl, key, KEY_LEN(key));
	ok(rval != NULL, "hhash: find existing element");

	/* Fill the table. */
	for (unsigned i = 0; i < ELEM_COUNT; ++i) {
		ret = hhash_insert(tbl, keys[i], KEY_LEN(keys[i]), keys[i]);
		if (ret != KNOT_EOK) {
			nfilled = i;
			break;
		}
	}

	/* Check all keys integrity. */
	unsigned nfound = 0;
	for (unsigned i = 0; i < nfilled; ++i) {
		rval = hhash_find(tbl, keys[i], KEY_LEN(keys[i]));
		if (!rval || memcmp(*rval, keys[i], KEY_LEN(keys[i])) != 0) {
			break; /* Mismatch */
		}
		++nfound;
	}
	is_int(nfilled, nfound, "hhash: found all inserted keys");

	/* Test keys order index. */
	hhash_build_index(tbl);
	hhash_iter_begin(tbl, &it, true);
	while (!hhash_iter_finished(&it)) {
		cur = hhash_iter_key(&it, &len);
		if (!str_check_sort(prev, cur)) {
			break;
		}
		prev = cur;
		int strl = strlen(cur);
		assert(strl + 1 == len);
		hhash_iter_next(&it);
	}
	ok(hhash_iter_finished(&it), "hhash: passed order index checks");

	/* Retrieve all keys. */
	nfound = 0;
	hhash_iter_begin(tbl, &it, false);
	while (!hhash_iter_finished(&it)) {
		cur = hhash_iter_key(&it, &len);
		if (hhash_find(tbl, cur, len) == NULL) {
			break;
		} else {
			++nfound;
		}
		hhash_iter_next(&it);
	}
	ok(hhash_iter_finished(&it), "hhash: found all iterated keys");
	is_int(tbl->weight, nfound, "hhash: all iterated keys found");

	/* Test find less or equal. */
	prev = "mykey0"; /* mykey should precede it */
	hhash_find_leq(tbl, prev, KEY_LEN(prev), &rval);
	ok(rval && *rval == val, "hhash: find less or equal");

	/* Delete key and retrieve it. */
	ret = hhash_del(tbl, key, KEY_LEN(key));
	ok(ret == KNOT_EOK, "hhash: remove key");
	rval = hhash_find(tbl, key, KEY_LEN(key));
	ok(rval == NULL, "hhash: find removed element");

	/* Free all memory. */
	mp_delete(mm.ctx);
	return KNOT_EOK;
}
/*
 * vs_columns --
 *	Return the screen columns necessary to display the line, or,
 *	if specified, the physical character column within the line.
 *
 * PUBLIC: size_t vs_columns __P((SCR *, CHAR_T *, db_recno_t, size_t *, size_t *));
 */
size_t
vs_columns(SCR *sp, CHAR_T *lp, db_recno_t lno, size_t *cnop, size_t *diffp)
{
	size_t chlen, cno, curoff, last = 0, len, scno;
	int ch, leftright, listset;
	CHAR_T *p;

	/*
	 * Initialize the screen offset.
	 */
	scno = 0;

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

	/* Need the line to go any further. */
	if (lp == NULL) {
		(void)db_get(sp, lno, 0, &lp, &len);
		if (len == 0)
			goto done;
	}

	/* Missing or empty lines are easy. */
	if (lp == NULL) {
done:		if (diffp != NULL)		/* XXX */
			*diffp = 0;
		return scno;
	}

	/* Store away the values of the list and leftright edit options. */
	listset = O_ISSET(sp, O_LIST);
	leftright = O_ISSET(sp, O_LEFTRIGHT);

	/*
	 * Initialize the pointer into the buffer and current offset.
	 */
	p = lp;
	curoff = 0;

	/* Macro to return the display length of any signal character. */
#define	CHLEN(val) (ch = *(UCHAR_T *)p++) == '\t' &&			\
	    !listset ? TAB_OFF(val) : KEY_COL(sp, ch);

	/*
	 * If folding screens (the historic vi screen format), past the end
	 * of the current screen, and the character was a tab, reset the
	 * current screen column to 0, and the total screen columns to the
	 * last column of the screen.  Otherwise, display the rest of the
	 * character in the next screen.
	 */
#define	TAB_RESET {							\
	curoff += chlen;						\
	if (!leftright && curoff >= sp->cols) {				\
		if (ch == '\t') {					\
			curoff = 0;					\
			scno -= scno % sp->cols;			\
		} else							\
			curoff -= sp->cols;				\
	}								\
}
	if (cnop == NULL)
		while (len--) {
			chlen = CHLEN(curoff);
			last = scno;
			scno += chlen;
			TAB_RESET;
		}
	else
		for (cno = *cnop;; --cno) {
			chlen = CHLEN(curoff);
			last = scno;
			scno += chlen;
			TAB_RESET;
			if (cno == 0)
				break;
		}

	/* Add the trailing '$' if the O_LIST option set. */
	if (listset && cnop == NULL)
		scno += KEY_LEN(sp, '$');

	/*
	 * The text input screen code needs to know how much additional
	 * room the last two characters required, so that it can handle
	 * tab character displays correctly.
	 */
	if (diffp != NULL)
		*diffp = scno - last;
	return (scno);
}