Streams message allocation

Larry McVoy lm at snafu.Sun.COM
Fri Aug 10 03:21:06 AEST 1990


In article <1990Aug2.043059.578 at cbnewsl.att.com> sar0 at cbnewsl.att.com (stephen.a.rago) writes:
>The number of messages needed per buffer class (size) is something that
>can only be determined statistically or empirically.  In the absence of
>information like inter-arrival rate of allocation requests and message
>hold times, the best way to proceed is to start with an educated guess
>of how many messages you may need.  

Here's a package I wrote a few years ago when tuning STREAMS for SCO XENIX
(I was porting the LAI TCP/IP to XENIX and tuning was really critical).

Use this while your system is under a "normal" load and it will give you a
pretty good idea of where to set things.  No promises that it works -
it worked on SCO XENIX a while ago, beyond that you're on your own.  If
you have SCO XENIX & TCP/IP you should have this program already.



# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# Makefile mode.c sw.1 sw.c term.h termcap.c

echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
O = sw.o termcap.o mode.o
S = Makefile sw.1 sw.c termcap.c mode.c term.h
#CFLAGS=-DUNIX=\"/xenix\" -DKMEM=\"/dev/mem\" -DSAVECNT=0
BIN=/usr/local/bin

all sw: $O
	cc $O -ltermlib -o sw 
install: sw
	cp sw $(BIN)
	chown root $(BIN)/sw
	chmod 4755 $(BIN)/sw
clean:
	rm -f $O a.out core shar
clobber: clean
	rm -f sw
shar:
	shar $S > shar
//E*O*F Makefile//

echo x - mode.c
cat > "mode.c" << '//E*O*F mode.c//'
/*
 * copyright (C) 1986 by Larry McVoy
 * MUST be distributed in source form only.
 */
# include	<stdio.h>
# include	<sgtty.h>
# include	<fcntl.h>

static struct sgttyb buf;
static done = 0;

delay(on)
{
    if (on) {
	int flags;
	fcntl(0, F_GETFL, &flags);
	flags &= ~O_NDELAY;
	return fcntl(0, F_SETFL, flags);
    }
    else {
	return fcntl(0, F_SETFL, O_NDELAY);
    }
}

cbreak(on)
{
    if (!done) {
	ioctl(fileno(stdin), TIOCGETP, &buf);
	done++;
    }

    if (on) {
	buf.sg_flags |= CBREAK;
	ioctl(fileno(stdin), TIOCSETP, &buf);
    }
    else {
	buf.sg_flags &= ~CBREAK;
	ioctl(fileno(stdin), TIOCSETP, &buf);
    }
}

echo(on)
{
    if (!done) {
	ioctl(fileno(stdin), TIOCGETP, &buf);
	done++;
    }

    if (on) {
	buf.sg_flags |= ECHO;
	ioctl(fileno(stdin), TIOCSETP, &buf);
    }
    else {
	buf.sg_flags &= ~ECHO;
	ioctl(fileno(stdin), TIOCSETP, &buf);
    }
}
//E*O*F mode.c//

echo x - sw.1
cat > "sw.1" << '//E*O*F sw.1//'
.TH SW 1
.UC 4
.SH NAME
sw - (stream watch) watch streams resources on System V
.SH SYNOPSIS
.B sw
.SH DESCRIPTION
.I Sw
digs into kmem to find out how many streams, queues, message blocks, and data
blocks are in use.  It find this in the \fIstruct strstat strst\fR variable.
For each category mentioned above the following fields are printed:
.IP Use 
(strst.use)
How many of the resource in question are in use.
.IP "Ave10, Ave30, Ave60, Ave120"
As above, only the value is averaged over the last N iterations 
(an iteration is
about one second).
.IP Total 
(strst.total)
The total number of the resource used since boot time or the last time it
was cleared.
.IP Max 
(strst.max)
The maximum number of the resource allocated at the same time.
.IP Fail 
(strst.fail)
The number of times a request was made, for the resource, that could not
be granted.
.PP
The data block section is further broken down into sub classes.  This section 
has a slightly different format; in the title section the field is 
\fISiz<#> Count <#>\fR, where the first number is the data block size and
the second number is the number of data blocks statically allocated.
.PP
The screen is managed by curses.  It will respond to:
.IP c
clear the total max & fail fields (you have to have write permission on
/dev/mem).
.IP ^L
redraw the screen.
.IP N
Where N is 0-9.  Set the number of seconds between interations.
.IP q
(quit) Quit the program.
.IP ^L
(Control-L) Refresh the screen.
.SH FILES
.DT
/dev/kmem	kernel memory
.br
/unix		for getting variable addresses
.SH BUGS
The definitions of the various fields is my best guess, they do not reflect
any AT&T documentation that I've read.
.SH COPYRIGHT
\fBSw\fR is copyright 1988 by Larry McVoy.  Permission is hereby granted to
publish strings in source or object form as long as all copyright notices
are retained.  Object-only distributions are permitted only if the source
is also freely available from the distributer.  Any fee charged for such
publication may consist only of a reasonable charge for any media used.
.SH AUTHOR
Larry McVoy (lm at eng.sun.com)
//E*O*F sw.1//

echo x - sw.c
cat > "sw.c" << '//E*O*F sw.c//'
/*
 * copyright 1988 by Larry McVoy.  All rights reserved.
 * If you redistribute this you must distribute in source form.
 */
#include "term.h"
#include <signal.h>
#ifdef M_XENIX
#include <a.out.h>
#else
#include <nlist.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strstat.h>
#include <sys/var.h>
#if defined(M_XENIX)  ||  defined(sys5)
#include <sys/utsname.h>
#endif
#ifndef UNIX
#define	UNIX		"/vmunix"
#endif
#ifndef KMEM
#define	KMEM		"/dev/kmem"
#endif
#ifdef M_XENIX
#define	v_nblk4096	v_nblk8192
#endif
#ifdef sun
#define NCLASS 9
#endif
#ifndef SAVECNT
#define SAVECNT		121
#endif
#define kbytes(x)	( ((x)+1023) >> 10)

#ifdef M_XENIX
#define nlist xlist
#define n_value xl_value
#define n_name xl_name
struct nlist nl[] = {
#define	NL_STRST	0
	{0,0,0,"_strst"},
#define	NL_RBSIZE	1
	{0,0,0,"_rbsize"},
#define	NL_V		2
	{0,0,0,"_v"},
#define	NL_DBALLOC	3
	{0,0,0,"_dballoc"},
#define	NL_NMBLOCK	4
	{0,0,0,"_nmblock"},
	{0,0,0,(char *) 0},
};
#else
#ifdef sys5
struct nlist nl[] = {
#define	NL_STRST	0
	{"_strst"},
#define	NL_RBSIZE	1
	{"_rbsize"},
#define	NL_V		2
	{"_v"},
#define	NL_DBALLOC	3
	{"_dballoc"},
#define	NL_NMBLOCK	4
	{"_nmblock"},
	{ 0 },
};
#else
#ifdef sun
char* nl_names[] = {
#define	NL_STRST	0
	"_strst",
#define	NL_RBSIZE	1
	"_rbsize",
#define	NL_NDBLKS	2
	"_ndblks",
#define	NL_DBALLOC	3
	"_dballoc",
#define	NL_NMBLOCK	4
	"_nmblock",
	"",
};

struct nlist nl[sizeof(nl_names)/sizeof(char*)];
#endif	/* sun */
#endif	/* sys5 */
#endif	/* M_XENIX */

ushort rbsize[NCLASS];
short cnt[NCLASS];
struct dbalcst dballoc[NCLASS];
int total, ndblock, nmblock, fd, sleeptime = 1;
#ifndef sun
struct var v;
#endif
typedef struct {
    alcdat stream;
    alcdat queue;
    alcdat mblock;
    alcdat dblock;
    alcdat dblk[NCLASS];
} Strstat;
Strstat strst;

/*
 * It's the main thing...
 */
main(ac, av)
    char** av;
{
    int* p;
    int i;
    int done();

    for (i=1; i<ac; ++i) {
	 if (av[i][0] == '-')
	    if (isdigit(av[i][1]))
		sleeptime = atoi(&av[i][1]);
    }
/*
 * get stuff from kmem
 */
# ifdef sun
    for (i=0; i<sizeof(nl_names)/sizeof(char*); ++i)
	nl[i].n_name = nl_names[i];
# endif
    if (nlist(UNIX, nl))
	error("nlist");
    if (((fd = open(KMEM, 2)) == -1)  && ((fd = open(KMEM, 0)) == -1))
	error(KMEM);
    readstuff();
# ifdef sun
    for (i=0; i<NCLASS; ndblock += cnt[i++])
	;
# else
    for (i=0, p= &v.v_nblk4096; i<NCLASS; ndblock += (cnt[NCLASS - ++i] = *p++))
	;
# endif
/*
 * screen/mode stuff
 */
    termcap();;
    echo(0);
    cbreak(1);
    delay(0);
    signal(SIGHUP, done);
    signal(SIGINT, done);
    signal(SIGQUIT, done);
    signal(SIGTERM, done);
    screen();
/*
 * display until quit
 */
    for ( ;; ) {
	if (lseek(fd, nl[NL_STRST].n_value, 0) == -1)
	    error("lseek");
	if (read(fd, &strst, sizeof(strst)) != sizeof(strst))
	    error("read");
	dump(&strst);
	for (i=0; i<sleeptime; ++i) {
	    if (input())
		break;
	    sleep(1);
	}
	input();
    }
}

/*
 * look for commands; expect to be in cbreak mode
 */
input()
{
    char c;

    if (read(0, &c, 1) != 1)
	return 0;
    switch (c) {
    case 'c':
	clearstuff();
	return 1;
    case '':
	screen();
	return 1;
    case 'q':
	done();
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
	sleeptime = c - '0';
	Pause();
	return 1;
    case '?':
    case 'h':
	clear();
	tprint("", 0, 0);
	printf("\nOptions are\n");
	printf("\tc\tclear total, max, and fail fields\n");
	printf("\t^L\trefresh the screen\n");
	printf("\tq\tquit\n");
	printf("\tN\twhere N is a number 0..9 for the delay between refresh\n");
	printf("Hit any key to continue....\n");
	while (read(0, &c, 1) != 1)
	    sleep(1);
	screen();
	return 1;
    default:
	return 0;
    }
}

/*
 * clear the total, max, and fail fields
 *
 * N.B. assumes acldat is use, total, max, fail
 * and there are NCLASS+4 acldat's in a strstat
 */
clearstuff()
{
    static int clr[3];
    register i;

    clear();
    lseek(fd, nl[NL_STRST].n_value+sizeof(int), 0);
    for (i=0; i<NCLASS+4; ++i) {
	if (write(fd, clr, sizeof(clr)) != sizeof(clr))
	    perror("write");
	lseek(fd, sizeof(int), 1);
    }
    screen();
}

/*
 * display the "once only" stuff
 */
screen()
{
    char buf[200];
    register i;
#if defined(M_XENIX)  ||  defined(sys5)
    struct utsname u;
#else
    char host[100];
#endif

    clear();
#if defined(M_XENIX)  ||  defined(sys5)
    uname(&u);
    sprintf(buf, "Host=%s", u.nodename);
#else
    gethostname(host, sizeof(host));
    sprintf(buf, "Host=%s", host);
#endif
    tprint(buf, 50, 0);
# if SAVECNT > 0
    sprintf(buf, "%11s%5s%6s%6s%6s%6s%7s%8s%8s%8s", 
	"Resource", "Cnt", "Use", "Ave10", "Ave30", "Ave60", "Ave120", "Total", 
	"Max", "Fail");
# else
    sprintf(buf, "%11s%5s%10s%8s%8s%8s", 
	"Resource", "Cnt", "Use", "Total", "Max", "Fail");
# endif
    tprint(buf, 0, 2);
# ifdef sun
    sprintf(buf, "%11s:%4s",  "stream", "?");
    tprint(buf, 0, 3);
    sprintf(buf, "%11s:%4s",  "queue", "?");
    tprint(buf, 0, 4);
# else
    sprintf(buf, "%11s:%4d",  "stream", v.v_nstream);
    tprint(buf, 0, 3);
    sprintf(buf, "%11s:%4d",  "queue", v.v_nqueue); 
    tprint(buf, 0, 4);
# endif
    sprintf(buf, "%11s:%4d",  "mblock", nmblock); 
    tprint(buf, 0, 5);
    sprintf(buf, "%11s:%4d",  "dblk totals", ndblock); 
    tprint(buf, 0, 6);
# if SAVECNT > 0
    sprintf(buf, "%4s%4s%4s%4s%6s%6s%6s%6s%7s%8s%8s%8s", 
	"Size", "Cnt", "Med", "Low", "Use", "Ave10", "Ave30", "Ave60", "Ave120", "Total", "Max", "Fail");
# else
    sprintf(buf, "%-4s%4s%4s%4s%4s%6s%8s%8s%8s", 
	"Mem", "Size", "Cnt", "Med", "Low", "Use", "Total", "Max", "Fail");
# endif
    tprint(buf, 0, 8);
    for (total=i=0; i<NCLASS; ++i) {
	register lo = dballoc[i].dba_lo;
	register med = dballoc[i].dba_med;

	total += rbsize[i] * cnt[i];
# if SAVECNT > 0
	sprintf(buf, "%4d %3d %3d %3d", rbsize[i], cnt[i], med, lo);
# else
	sprintf(buf, "%3d %4d %3d %3d %3d", kbytes(rbsize[i] * cnt[i]), 
	    rbsize[i], cnt[i], med, lo);
# endif
	tprint(buf, 0, 9 + i);
    }
    sprintf(buf, "Buffers (used/total) = ");
    tprint(buf, 0, 23);
    Pause();
}

Pause()
{
    char buf[40];

    sprintf(buf, "Pause=%d", sleeptime);
    tprint(buf, 0, 0);
}

# if SAVECNT == 0
/*
 * display the information, called once per second (about)
 *
 * No averaging version
 */
dump(s)
    register struct strstat* s;
{
    char buf[80];
    register i, mem = 0;
    static calls = 0;

    sprintf(buf, "%6d%8d%8d%8d",
	s->stream.use, s->stream.total, s->stream.max, s->stream.fail);
    tprint(buf, 20, 3);
    sprintf(buf, "%6d%8d%8d%8d",
	s->queue.use, s->queue.total, s->queue.max, s->queue.fail);
    tprint(buf, 20, 4);
    sprintf(buf, "%6d%8d%8d%8d",
	s->mblock.use, 
	s->mblock.total, s->mblock.max, s->mblock.fail);
    tprint(buf, 20, 5);
    sprintf(buf, "%6d%8d%8d%8d",
	s->dblock.use, s->dblock.total, s->dblock.max, s->dblock.fail);
    tprint(buf, 20, 6);
    for (i=0; i<NCLASS; ++i) {
	mem += s->dblk[i].use * rbsize[i];
	sprintf(buf, "%6d%8d%8d%8d",
	    s->dblk[i].use, s->dblk[i].total, s->dblk[i].max, s->dblk[i].fail);
	tprint(buf, 20, 9 + i);
    }
    sprintf(buf, "%d/%d Kbytes", kbytes(mem), kbytes(total));
    tprint(buf, 23, 23);
    calls++;
    sprintf(buf, "Calls=%d", calls);
    tprint(buf, 10, 0);
}
# else
/*
 * display the information, called once per second (about)
 *
 * Averaging version
 */
dump(s)
    register struct strstat* s;
{
    char buf[80];
    register i, j, b10, b30, b60, b120, mem = 0;
    static struct strstat pst[SAVECNT];
    static struct strstat sum10, sum30, sum60, sum120;
    static calls = 0;

    j = calls % SAVECNT;
    b10 = (j + SAVECNT - 10) % SAVECNT;
    b30 = (j + SAVECNT - 30) % SAVECNT;
    b60 = (j + SAVECNT - 60) % SAVECNT;
    b120 = (j + SAVECNT - 120) % SAVECNT;
    memcpy(&pst[j], s, sizeof(struct strstat));
    addstrst(s, &sum10);
    addstrst(s, &sum30);
    addstrst(s, &sum60);
    addstrst(s, &sum120);
    if (!calls) {
	mulstrst(&sum10, 10);
	mulstrst(&sum30, 30);
	mulstrst(&sum60, 60);
	mulstrst(&sum120, 120);
	for (i=1; i<SAVECNT; ++i)
	    memcpy(&pst[i], s, sizeof(struct strstat));
    }
    else {
	substrst(&pst[b10], &sum10);
	substrst(&pst[b30], &sum30);
	substrst(&pst[b60], &sum60);
	substrst(&pst[b120], &sum120);
    }
    sprintf(buf, "%6d%6d%6d%6d%7d%8d%8d%8d",
	s->stream.use, 
	sum10.stream.use / 10, sum30.stream.use / 30, sum60.stream.use / 60,
	sum120.stream.use / 120,
	s->stream.total, s->stream.max, s->stream.fail);
    tprint(buf, 16, 3);
    sprintf(buf, "%6d%6d%6d%6d%7d%8d%8d%8d",
	s->queue.use, 
	sum10.queue.use / 10, sum30.queue.use / 30, sum60.queue.use / 60,
	sum120.queue.use / 120,
	s->queue.total, s->queue.max, s->queue.fail);
    tprint(buf, 16, 4);
    sprintf(buf, "%6d%6d%6d%6d%7d%8d%8d%8d",
	s->mblock.use, 
	sum10.mblock.use / 10, sum30.mblock.use / 30, sum60.mblock.use / 60,
	sum120.mblock.use / 120,
	s->mblock.total, s->mblock.max, s->mblock.fail);
    tprint(buf, 16, 5);
    sprintf(buf, "%6d%6d%6d%6d%7d%8d%8d%8d",
	s->dblock.use, 
	sum10.dblock.use / 10, sum30.dblock.use / 30, sum60.dblock.use / 60,
	sum120.dblock.use / 120,
	s->dblock.total, s->dblock.max, s->dblock.fail);
    tprint(buf, 16, 6);
    for (i=0; i<NCLASS; ++i) {
	mem += s->dblk[i].use * rbsize[i];
	sprintf(buf, "%6d%6d%6d%6d%7d%8d%8d%8d",
	    s->dblk[i].use, 
	    sum10.dblk[i].use / 10, sum30.dblk[i].use / 30, 
	    sum60.dblk[i].use / 60, sum120.dblk[i].use / 120,
	    s->dblk[i].total, s->dblk[i].max, s->dblk[i].fail);
	tprint(buf, 16, 9 + i);
    }
    sprintf(buf, "%d/%d Kbytes", kbytes(mem), kbytes(total));
    tprint(buf, 23, 23);
    calls++;
    sprintf(buf, "Calls=%d", calls);
    tprint(buf, 10, 0);
}

/*
 * add a to be, leave result in b
 *
 * N.B. Assumes that elements are ints
 */
addstrst(a, b)
    register alcdat* a;
    register alcdat* b;
{
    register i;

    for (i=sizeof(struct strstat)/sizeof(*a); i--; (b++)->use += (a++)->use)
	;
}

/*
 * subtract a from be, result in b
 */
substrst(a, b)
    register alcdat* a;
    register alcdat* b;
{
    register i;

    for (i=sizeof(struct strstat)/sizeof(*a); i--; (b++)->use -= (a++)->use)
	;
}

/*
 * multiply a by b, result in a
 */
mulstrst(a, b)
    register alcdat* a;
    register int b;
{
    register i;

    for (i=sizeof(struct strstat)/sizeof(*a); i--; (a++)->use *= b)
	;
}
# endif	/* SAVECNT */

/*
 * nasty cleanup
 */
error(s)
    char* s;
{
    perror(s);
    done();
}

/*
 * nice cleanup
 */
done()
{
    echo(1);
    cbreak(0);
    delay(1);
    tprint("", cols -1, lines - 1);
    printf("\n");
    exit(0);
}

/*
 * read all once only stuff from kmem
 */
readstuff()
{
    register i;
    if (!nl[NL_RBSIZE].n_value) {
	printf("%s: no value\n", nl[NL_RBSIZE].n_name);
	exit(1);
    }
    if (!nl[NL_STRST].n_value) {
	printf("%s: no value\n", nl[NL_STRST].n_name);
	exit(1);
    }
# ifdef sun
    if (!nl[NL_NDBLKS].n_value) {
	printf("%s: no value\n", nl[NL_NDBLKS].n_name);
# else
    if (!nl[NL_V].n_value) {
	printf("%s: no value\n", nl[NL_V].n_name);
# endif
	exit(1);
    }
    if (!nl[NL_DBALLOC].n_value) {
	printf("%s: no value\n", nl[NL_DBALLOC].n_name);
	exit(1);
    }
    if (!nl[NL_NMBLOCK].n_value) {
	printf("%s: no value\n", nl[NL_NMBLOCK].n_name);
	exit(1);
    }
    if (lseek(fd, nl[NL_RBSIZE].n_value, 0) == -1)
	error("lseek");
    if (read(fd, rbsize, sizeof(rbsize)) != sizeof(rbsize))
	error("read");
# ifdef sun
    if (lseek(fd, nl[NL_NDBLKS].n_value, 0) == -1)
	error("lseek");
    if (read(fd, cnt, sizeof(cnt)) != sizeof(cnt))
	error("read");
# else
    if (lseek(fd, nl[NL_V].n_value, 0) == -1)
	error("lseek");
    if (read(fd, &v, sizeof(v)) != sizeof(v))
	error("read");
# endif
# ifdef sun
    if (lseek(fd, nl[NL_DBALLOC].n_value, 0) == -1)
	error("lseek");
    if (read(fd, &nl[NL_DBALLOC].n_value, sizeof(int)) != sizeof(int))
	error("read");
# endif
    if (lseek(fd, nl[NL_DBALLOC].n_value, 0) == -1)
	error("lseek");
    if (read(fd, dballoc, sizeof(dballoc)) != sizeof(dballoc))
	error("read");
    if (lseek(fd, nl[NL_NMBLOCK].n_value, 0) == -1)
	error("lseek");
    if (read(fd, &nmblock, sizeof(nmblock)) != sizeof(nmblock))
	error("read");
# ifdef sun
    if (lseek(fd, nl[NL_STRST].n_value, 0) == -1)
	error("lseek");
    if (read(fd, &nl[NL_STRST].n_value, sizeof(int)) != sizeof(int))
	error("read");
# endif
}
//E*O*F sw.c//

echo x - term.h
cat > "term.h" << '//E*O*F term.h//'
# ifndef	_TERM_H_
#include <stdio.h>
#include <sgtty.h>

char	*getenv();
char	*tgetstr();
char	PC;
short	ospeed;
short	lines;
short	cols;
char 	ceolbuf[20];
char 	clbuf[20];
char 	pcbuf[20];
char 	cmbuf[20];
#undef	putchar
int	putchar();
char 	term_buf[1024];

# endif	_TERM_H_
//E*O*F term.h//

echo x - termcap.c
cat > "termcap.c" << '//E*O*F termcap.c//'
/*
 * copyright (C) 1986 by Larry McVoy
 * MUST be distributed in source form only.
 */
# include	"term.h"

char* tgoto();

/*------------------------------------------------------------------15/Dec/86-*
 * init_term - read in the termcap stuff
 *----------------------------------------------------------------larry mcvoy-*/
termcap()
{
    char *cp = getenv("TERM");
    char *foo;
    char garbage[10];
    struct sgttyb tty;

    gtty(1, &tty);
    ospeed = tty.sg_ospeed;
    if (cp == (char *) 0)
	    return -1;
    if (tgetent(term_buf, cp) != 1)
	    exit(1);
    foo = garbage;
    foo = tgetstr("pc", &foo);
    if (foo)
	    PC = *foo;
    foo = clbuf;
    tgetstr("cl", &foo);
    foo = ceolbuf;
    tgetstr("ce", &foo);
    foo = cmbuf;
    tgetstr("cm", &foo);
    lines = tgetnum("li");
    cols = tgetnum("co");
    return 0;
}

/* clear to end of line */
ceol(col, row)
{
    char *foo = tgoto(cmbuf, col, row);

    write(1, foo, strlen(foo));
    tputs(ceolbuf, 1, putchar);
}

/* clear screen */
clear()
{
    tputs(clbuf, lines, putchar);
}

/*------------------------------------------------------------------15/Dec/86-*
* tputchar(c, col, row) - put a single char on the screen
*
* Inputs ----> (char), (int), (int)
*
* Bugs ------> Assumes that the coords make sense
*
* Revisions:   
*----------------------------------------------------------------larry mcvoy-*/
tputchar(c, col, row)
    char c;
{
    register char* foo;

    foo = tgoto(cmbuf, col, row);
    tputs(foo, lines, putchar);
    write(1, &c, 1);
}

/*------------------------------------------------------------------15/Dec/86-*
* tprint(s, col, row) - put a string on the screen
*
* Inputs ----> (char), (int), (int)
*
* Results ---> Puts the string out iff it will fit.
*
* Revisions:   
*----------------------------------------------------------------larry mcvoy-*/
tprint(s, col, row)
    register char* s;
{
    register char* foo;

    if (row > lines  ||  col > cols)
	return -1;

    if (strlen(s) > cols - col)
	return -2;

    foo = tgoto(cmbuf, col, row);
    tputs(foo, lines, putchar);
    return write(1, s, strlen(s));
}

/* fake putchar for tputs */
putchar(c)
char c;
{
    write(1, &c, 1);
}
//E*O*F termcap.c//

echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      17      53     336 Makefile
      57     120     878 mode.c
      63     359    2087 sw.1
     563    1521   12403 sw.c
      19      36     278 term.h
     104     281    2203 termcap.c
     823    2370   18185 total
!!!
wc  Makefile mode.c sw.1 sw.c term.h termcap.c | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
---
Larry McVoy, Sun Microsystems     (415) 336-7627       ...!sun!lm or lm at sun.com



More information about the Comp.unix.wizards mailing list