Ultrix-3.1/src/cmd/uucp/pk0.c

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


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

static char Sccsid[] = "@(#)pk0.c	3.0	4/22/86";


/*
 * packet driver
 */

/* decvax!larry - added significant number of comments */

extern	char	*malloc();

#define USER	1
#include <stdio.h>
#include <sys/param.h>
#include <sys/buf.h>
#ifdef	PADDR
#undef	PADDR
#endif	PADDR
#include "pk.p"
#include "pk.h"



char next[8]	={ 1,2,3,4,5,6,7,0};	/* packet sequence numbers */
char mask[8]	={ 1,2,4,010,020,040,0100,0200 };

struct pack *pklines[NPLINES];


/*
 * receive control messages
 * pkcntl() implements the protocol "state machine"
 * - the initialization stage consists of three steps:
 *  1. send window size (and receive window size) - INITA
 *  2. send packet size (and receive packet size) - INITAB
 *  3. send final ack. - INITC
 * - after the init stage data can be sent or received.
 * - data is transferred in packets until the window is reached
 * - acks are returned for each packet received after which time
 *	another packet can be sent.
 * 
 */
pkcntl(c, pk)
register struct pack *pk;
{
register cntl, val;

	val = c & MOD8;
	cntl = (c>>3) & MOD8;

	if ( ! ISCNTL(c) ) {
		fprintf(stderr, "not cntl\n");
		return;
	}

	if (pk->p_mode & 02)
		fprintf(stderr, "%o ",c);
	switch(cntl) {

	case INITB:
		/*   get packet size */
		val++;
		pk->p_xsize = pksizes[val];
		pk->p_lpsize = val;
		pk->p_bits = DTOM(pk->p_xsize);
		if (pk->p_state & LIVE) {
			pk->p_msg |= M_INITC;
			break;
		}
		pk->p_state |= INITb;
		if ((pk->p_state & INITa)==0) {

		/*   if remote side has not entered state A it has not 
 		*   received our original control message.
 		*   therefore send original message again
 		*/

#ifdef DEBUGOUT
		fprintf(stderr, "not in A got INITB: state=%o, p_msg=%o, p_rmsg=%o, p_swindow=%o\n",
			pk->p_state, pk->p_msg, pk->p_rmsg, pk->p_swindow);
#endif
			break;
		}
		/* sync'ed up so far. Try for final state: INITC - send ack. */
		pk->p_rmsg &= ~M_INITA;
		pk->p_msg |= M_INITC;
#ifdef DEBUGOUT
		fprintf(stderr, "In A got INITB: state=%o, p_msg=%o, p_rmsg=%o, p_swindow=%o\n",
			pk->p_state, pk->p_msg, pk->p_rmsg, pk->p_swindow);
#endif
		break;

	case INITC:
		/* get final ack */
		if ((pk->p_state&INITab)==INITab) {
			/*  both sides are now ready */
			pk->p_state = LIVE;
			WAKEUP(&pk->p_state);
			pk->p_rmsg &= ~M_INITB;
#ifdef DEBUGOUT
			fprintf(stderr, "In AB got INITC: state=%o, p_msg=%o, p_rmsg=%o, p_swindow=%o\n",
			pk->p_state, pk->p_msg, pk->p_rmsg, pk->p_swindow);
#endif
		} else {
			/* remote did not receive INITB message */
			pk->p_msg |= M_INITB;
#ifdef DEBUGOUT
			fprintf(stderr, "Not in AB got INITB: state=%o, p_msg=%o, p_rmsg=%o, p_swindow=%o\n",
			pk->p_state, pk->p_msg, pk->p_rmsg, pk->p_swindow);
#endif
		}
		if (val)
			pk->p_swindow = val;
		break;
	case INITA:
		if (val==0 && pk->p_state&LIVE) {
			fprintf(stderr, "alloc change not implemented\n");
			break;
		}
		/* val is the recive window at the remote site */
		if (val) {
			pk->p_state |= INITa;
			pk->p_msg |= M_INITB;
			pk->p_rmsg |= M_INITB;
			pk->p_swindow = val;
#ifdef DEBUGOUT
			fprintf(stderr, "received INITA: state=%o, p_msg=%o, p_rmsg=%o, p_swindow=%o\n",
			pk->p_state, pk->p_msg, pk->p_rmsg, pk->p_swindow);
#endif
		}
		break;
	case RJ:   /* packet was rejected at remote site - send again */
#ifdef DEBUGOUT
		fprintf(stderr, "pkcntl, case=RJ\n");
#endif
		Totalrxmts++;
		pk->p_state |= RXMIT;
		pk->p_msg |= M_RR;
	case RR:
		/* packet was acknowledged or was a dup packet.
		 * In either case we dont have to send it again.
		 */

#ifdef DEBUGOUT
		fprintf(stderr, "pkcntl, case=RR\n");
#endif
		pk->p_rpr = val;
		if (pksack(pk)==0) {
			WAKEUP(&pk->p_ps);
		}
		break;
	case SRJ:
		fprintf(stderr, "srj not implemented\n");
		break;
	case CLOSE:
#ifdef DEBUGOUT
		fprintf(stderr, "pkcntl, case=CLOSE\n");
#endif
		pk->p_state = DOWN+RCLOSE;
		SIGNAL;
		WAKEUP(&pk->p_pr);
		WAKEUP(&pk->p_ps);
		WAKEUP(&pk->p_state);
		return;
	}
out:
	/* send response */
	if (pk->p_msg)
		pkoutput(pk);
}

/* pkaccept is equivalent to ttyinput. i.e. it is the part
 * of the line discipline which reads in the data off 
 * the network and wakes up the higher level pkread routine.
 * Of course this is not quite the way it goes since it is no
 * longer is a line discipline.
 */

pkaccept(pk)
register struct pack *pk;
{
register x,seq;
char m, cntl, *p, imask, **bp;
int bad,accept,skip,s,t,h,cc;
unsigned short sum;


	bad = accept = skip = 0;
	/*
	 * wait for input
	 */
	LOCK;
	x = next[pk->p_pr];   /* pk->p_pr is the last packet# received */

	/* wait for packet to arrive 
	 *  p_imap == 0 implies no data has arrived
	 */

	while ((imask=pk->p_imap) == 0 && pk->p_rcount==0) {
	/* p_imap is a bit map of input buffers
	 * rcount is the # of active receive buffers
	 */

#ifdef DEBUGOUT
		fprintf(stderr, "in pkaccept-get another packet\n");
#endif

		PKGETPKT(pk);
		/* pkgetpack will call pkdata which sets a bit in 
		 * imap when a packet arrives
		 */
		SLEEP(&pk->p_pr, PKIPRI);
	}
	pk->p_imap = 0;
	UNLOCK;


	/*
	 * determine input window in m.
	 */
	t = (~(-1<<(int)(pk->p_rwindow))) <<x;
	m = t;
	m |= t>>8;
#ifdef DEBUGOUT
	fprintf(stderr,"pkaccept,m=%o,t=%o\n",(int)m,(int)t);
#endif


	/*
	 * mark newly accepted input buffers
	 */
	for(x=0; x<8; x++) {

		if ((imask & mask[x]) == 0)
			continue; /* nothing has arrived in buffer x */

		if (((cntl=pk->p_is[x])&0200)==0) {
			/* if bad buffer then release space allocated to it */
			bad++;
free:
#ifdef DEBUGOUT
			fprintf(stderr,"read bad packet?\n");
#endif
			bp = (char **)pk->p_ib[x];
			LOCK;
			*bp = (char *)pk->p_ipool;
			pk->p_ipool = bp;
			pk->p_is[x] = 0;
			UNLOCK;
			continue;
		}

		pk->p_is[x] = ~(B_COPY+B_MARK);
		sum = (unsigned)chksum(pk->p_ib[x], pk->p_rsize) ^ (unsigned)(cntl&0377);
		sum += pk->p_isum[x];
		if (sum == CHECK) {
			seq = (cntl>>3) & MOD8; /* seq# of packet */
			if (m & mask[seq]) {
				if (pk->p_is[seq] & (B_COPY | B_MARK)) {
				dup:
#ifdef DEBUGOUT
					fprintf(stderr,"read in a duplicate packet\n");
#endif
					pk->p_msg |= M_RR;
					skip++;
					goto free;
				}
				if (x != seq) {
#ifdef DEBUGOUT
				fprintf(stderr,"seq number is = %d\n", seq);
#endif
				/* switch to current buffer? */
					LOCK;
					p = pk->p_ib[x];
					pk->p_ib[x] = pk->p_ib[seq];
					pk->p_is[x] = pk->p_is[seq];
					pk->p_ib[seq] = p;
					UNLOCK;
				}
				/* indicate buffer in use ? */
				pk->p_is[seq] = B_MARK;
				accept++;  /* inc # of accepted buffers */
				cc = 0;
				if (cntl&B_SHORT) {
				/* data fits in one buffer */
#ifdef DEBUGOUT
					fprintf(stderr,"short packet received\n");
#endif
					pk->p_is[seq] = B_MARK+B_SHORT;
					p = pk->p_ib[seq];
					cc = (unsigned)*p++ & 0377;
					if (cc & 0200) {
						cc &= 0177;
						cc |= *p << 7;
					}
				}
				pk->p_isum[seq] = pk->p_rsize - cc;
			} else {
				goto dup;
			}
		} else {
			bad++;
			/* check sum did not check out */
			goto free;
		}
	}

	/*
	 * scan window again turning marked buffers into
	 * COPY buffers and looking for missing sequence
	 * numbers.
	 */
	accept = 0;
	for(x=next[pk->p_pr],t=h= -1; m & mask[x]; x = next[x]) {
		if (pk->p_is[x] & B_MARK)
			pk->p_is[x] |= B_COPY;
	/*  hole code 
		if (pk->p_is[x] & B_COPY) {
			if (h<0 && t>=0)
				h = x;
		} else {
			if (t<0)
				t = x;
		}
	*/
		if (pk->p_is[x] & B_COPY) {
			if (t >= 0) {
				bp = (char **)pk->p_ib[x];
				LOCK;
				*bp = (char *)pk->p_ipool;
				pk->p_ipool = bp;
				pk->p_is[x] = 0;
				UNLOCK;
#ifdef DEBUGOUT
				fprintf(stderr,"pkaccept, skip=%d\n",skip);
#endif
				skip++;
			} else 
				accept++;
		} else {
			if (t<0)
				t = x;
		}
	}

	if (bad) {
		pk->p_msg |= M_RJ;
	}

	if (skip) {
		pk->p_msg |= M_RR;
	}

	pk->p_rcount = accept;
	return(accept);
}

/* pkread() is the high level input routine of a potential
 * line discipline.  pkaccept is called to read in data off
 * the network.  pkaccept should "wakeup" pkread when
 * all of the data is read in or there is no more buffer 
 * space. Pkread will move the data into user space and
 * return to high level routine that evoked it. The
 * higher level routine will have to reevoke pkread
 * if there is more data to read in.
 * pkread() returns the number characters read in.
 */

pkread(S)
SDEF;  /* struct pack *ipk, char *ibuf, int icount  */
{
register struct pack *pk;
register x,s;
int is,cc,xfr,count;
char *cp, **bp;

	pk = PADDR;
	xfr = 0;
	count = 0;
 	/* read data off the line */
	while (pkaccept(pk)==0);


	/* move data to user space and free up buffers */

	while (UCOUNT) { /* UCOUNT==icount */

		x = next[pk->p_pr];
		is = pk->p_is[x];

		if (is & B_COPY) {
			cc = MIN(pk->p_isum[x], UCOUNT);
			if (cc==0 && xfr) {
				break;
			}
			if (is & B_RESID)
				cp = pk->p_rptr;
			else {
				cp = pk->p_ib[x];
				if (is & B_SHORT) {
					if (*cp++ & 0200)
						cp++;
				}
			}
			IOMOVE(cp,cc,B_READ);
			count += cc;
			xfr++;
			pk->p_isum[x] -= cc;
			if (pk->p_isum[x] == 0) {
			/* free up buffers */
#ifdef DEBUGOUT
				fprintf(stderr,"pkread, packet read, x=%d\n",x);
#endif
				pk->p_pr = x;
				bp = (char **)pk->p_ib[x];
				LOCK;
				*bp = (char *)pk->p_ipool;
				pk->p_ipool = bp;
				pk->p_is[x] = 0;
				pk->p_rcount--;
				UNLOCK;
				pk->p_msg |= M_RR;
			} else {
				pk->p_rptr = cp+cc;
				pk->p_is[x] |= B_RESID;
			}
			if (cc==0)
				break;
		} else
			break;
	}
	pkoutput(pk);  /* xmit acknowledge of data to sender */
	return(count);
}



/* transmit as much data as possible i.e until exceed window
 * return with count of bytes sent.  
 * checksum data in buffers
 */

pkwrite(S)
SDEF;  /* struct pack *ipk, char *ibuf, int icount */
{
register struct pack *pk;
register x;
int partial;
caddr_t cp;
int cc, s, fc, count;

	pk = PADDR;
	if (pk->p_state&DOWN || !pk->p_state&LIVE) {
	/* connection is down */
		SETERROR;
		return(-1);
	}

	count = UCOUNT;
	do {
		LOCK;  /* null at user level */
		while (pk->p_xcount>=pk->p_swindow)  {

			/* out of buffer space */
#ifdef DEBUGOUT
			fprintf(stderr,"pkwrite, no more transmit buffers?\n");
#endif
			/* try to evoke ack of previously sent packets? */

			pkoutput(pk);

			/* wait for an ack to free up buffers */

			PKGETPKT(pk);
			SLEEP(&pk->p_ps,PKOPRI);
		}
		/* get seq# of next packet buffer to xmit */
		x = next[pk->p_pscopy];
		while (pk->p_os[x]!=B_NULL)  { /* buffer in use -wait for ack */
			PKGETPKT(pk);
			SLEEP(&pk->p_ps,PKOPRI);
		}
		/* reserve output buffer */
		pk->p_os[x] = B_MARK;
		pk->p_pscopy = x;
		/* increment # of active transmit buffers */
		pk->p_xcount++;
		UNLOCK;  /* null at user level */

		cp = pk->p_ob[x] = GETEPACK;  /* allocate space for buffer */
		partial = 0;
		if ((int)UCOUNT < pk->p_xsize) {
		/* data can fit into one buffer */
			cc = UCOUNT;
			fc = pk->p_xsize - cc;
			*cp = fc&0177;
			if (fc > 127) {
				*cp++ |= 0200;
				*cp++ = fc>>7;
			} else
				cp++;
			partial = B_SHORT;
#ifdef DEBUGOUT
			fprintf(stderr,"pkwrite, a short message\n");
#endif
		} else {
			/* need more than one buffer to xmit data */
			cc = pk->p_xsize;
#ifdef DEBUGOUT
			fprintf(stderr,"pkwrite, a long message\n");
#endif
		}
		/* get data from user area */
		IOMOVE(cp,cc,B_WRITE);
 		/* checksum data */
		pk->p_osum[x] = chksum(pk->p_ob[x], pk->p_xsize);
		pk->p_os[x] = B_READY+partial;
		pkoutput(pk);
	} while (UCOUNT);

	return(count);
}

/* free buffer space for data that has been acknowledged */
pksack(pk)
register struct pack *pk;
{
register x, i;

	i = 0;
	for(x=pk->p_ps; x!=pk->p_rpr; ) {
		x = next[x];
		if (pk->p_os[x]&B_SENT) {
#ifdef DEBUGOUT
			fprintf(stderr,"pksack, release buffer space\n");
#endif
			i++;
			pk->p_os[x] = B_NULL;
			pk->p_state &= ~WAITO;
			pk->p_xcount--;
			FREEPACK(pk->p_ob[x], pk->p_bits);
			pk->p_ps = x;
			WAKEUP(&pk->p_ps);
		}
	}
	return(i);
}



/* determine nature of data to be sent and tranmsit.
 * interpret control messages and send required data
 */


pkoutput(pk)
register struct pack *pk;
{
register x,rx;
int s;
char bstate;
int i;
SDEF;
int flag;

	flag = 0;
	ISYSTEM;
	LOCK;
	/* if line is busy don't use.  - can not happen though */
	if (pk->p_obusy++ || OBUSY) {
		pk->p_obusy--;
		UNLOCK;
		return;
	}
	UNLOCK;


	/*
	 * find seq number and buffer state
	 * of next output packet
	 */
	if (pk->p_state&RXMIT)  {
		/* if retransmitting then resend the next
		 * packet expected by the receiver.
		 * pk->p_rpr is the last packet received by the receiver.
		 */
		pk->p_nxtps = next[pk->p_rpr];
		flag++; /* not used anywhere */
	}
	x = pk->p_nxtps;   /* seq# of next buffer to transmit */
	bstate = pk->p_os[x]; /* output status of latter buffer */


	/*
	 * Send control packet if indicated
	 */
	if (pk->p_msg) {
		if (pk->p_msg & ~M_RR || !(bstate&B_READY) ) {
			x = pk->p_msg;
			for(i=0; i<8; i++) 
				if (x&1)
					break; else
				x >>= 1;
			x = i;
			x <<= 3;
			switch(i) {
			case CLOSE:
#ifdef DEBUGOUT
				fprintf(stderr, "pkoutput: send CLOSE\n");
#endif
				break;
			case RJ:  /* reject message */
#ifdef DEBUGOUT
				fprintf(stderr, "pkoutput: send RJ: \n");
#endif
			case RR:  /* dup or positive ack or timout*/
#ifdef DEBUGOUT
				fprintf(stderr, "pkoutput: send RR: \n");
#endif
				/* send seq# of last packet received */
				x += pk->p_pr;
				break;
			case SRJ:
#ifdef DEBUGOUT
				fprintf(stderr, "pkoutput: send SRJ: \n");
#endif
				break;
			case INITB:  /* send receive packet size */
				x += pksize(pk->p_rsize);
#ifdef DEBUGOUT
				fprintf(stderr, "pkoutput: send INITB: x=%o\n",x);
#endif
				break;
			case INITC:  /* send receive window size */
				     /* used as positive ack 	*/
				x += pk->p_rwindow;
#ifdef DEBUGOUT
				fprintf(stderr, "pkoutput: send INITC: x=%o\n",x);
#endif
				break;
			case INITA:   /* send receive window size */
				x += pk->p_rwindow;
#ifdef DEBUGOUT
				fprintf(stderr, "pkoutput: send INITA: x=%o\n",x);
#endif
				break;
			}
			pk->p_msg &= ~mask[i]; /* erase prev. control message */
#ifdef DEBUGOUT
			fprintf(stderr, "pkoutput: pk->p_msg=%o\n", pk->p_msg);
#endif
			pkxstart(pk, x, -1); /* start output */
			goto out;
		}
	}


	/*
	 * Don't send data packets if line is marked dead.
	 */
	if (pk->p_state&DOWN) {
		WAKEUP(&pk->p_ps);
		goto out;
	}
	/*
	 * Start transmission (or retransmission) of data packets.
	 */
	if (bstate & (B_READY|B_SENT)) {
		char seq;

		Totalpackets++;
		bstate |= B_SENT;
		seq = x;
		pk->p_nxtps = next[x]; /* next buffer for output */

		/* x=  |short/long |seq# of packet|last recv. #|  */
		x = 0200+pk->p_pr+(seq<<3);
		if (bstate & B_SHORT)  /* complete message in one packet */
			x |= 0100;
#ifdef DEBUGOUT
		fprintf(stderr,"pkoutput, transmit another packet\n");
#endif
		pkxstart(pk, x, seq);
		pk->p_os[seq] = bstate;  /* set initialy by pkwrite */
		pk->p_state &= ~RXMIT;
		pk->p_nout++;  /* total number of packets transmitted */
		goto out;
	}
	/*
	 * enable timeout if there's nothing to send
	 * and transmission buffers are languishing
	 * - does not appear to be used anywhere though
	 */
#ifdef DEBUGOUT
	fprintf(stderr,"pkoutput: nothing to transmit, pk->p_xcount=%d\n", pk->p_xcount);
#endif
	if (pk->p_xcount) {
		pk->p_timer = 2;
		pk->p_state |= WAITO;  /* WAITO not referenced anywhere else */
				       /*  at user level */
	} else

		pk->p_state &= ~WAITO;
	WAKEUP(&pk->p_ps);
out:
	pk->p_obusy = 0;
}


/*
 * shut down line by
 *	ignoring new input
 *	letting output drain
 *	releasing space and turning off line discipline
 */
pkclose(S)
SDEF;
{
register struct pack *pk;
register i,s,rbits;
char **bp;
int rcheck;
char *p;


	pk = PADDR;
	pk->p_state |= DRAINO;


	/*
	 * try to flush output
	 */
	i = 0;
	LOCK;
	pk->p_timer = 2;
#ifdef DEBUGOUT
	fprintf(stderr,"pkclose\n");
#endif
	while (pk->p_xcount && pk->p_state&LIVE) {
		if (pk->p_state&(RCLOSE+DOWN) || ++i > 2)
			break;
		pkoutput(pk);
		SLEEP(&pk->p_ps,PKOPRI);
	}
	pk->p_timer = 0;
	pk->p_state |= DOWN;
	UNLOCK;


	/*
	 * try to exchange CLOSE messages
	 */
	i = 0;
	while ((pk->p_state&RCLOSE)==0 && i<2) {
		pk->p_msg = M_CLOSE;
		pk->p_timer = 2;
		pkoutput(pk);
		SLEEP(&pk->p_ps, PKOPRI);
		i++;
	}


	for(i=0;i<NPLINES;i++)
		if (pklines[i]==pk)  {
			pklines[i] = NULL;
		}
	TURNOFF;


	/*
	 * free space
	 */
	rbits = DTOM(pk->p_rsize);
	rcheck = 0;
	for (i=0;i<8;i++) {
		if (pk->p_os[i]!=B_NULL) {
			FREEPACK(pk->p_ob[i],pk->p_bits);
			pk->p_xcount--;
		}
		if (pk->p_is[i]!=B_NULL)  {
			FREEPACK(pk->p_ib[i],rbits);
			rcheck++;
		}
	}
	LOCK;
	while (pk->p_ipool != NULL) {
		bp = pk->p_ipool;
		pk->p_ipool = (char **)*bp;
		rcheck++;
		FREEPACK(bp, rbits);
	}
	UNLOCK;
	if (rcheck  != pk->p_rwindow) {
		fprintf(stderr, "r short %d want %d\n",rcheck,pk->p_rwindow);
		fprintf(stderr, "rcount = %d\n",pk->p_rcount);
		fprintf(stderr, "xcount = %d\n",pk->p_xcount);
	}
	FREEPACK((caddr_t)pk, npbits);
}



/* reset pointers to buffer pool */
pkreset(pk)
register struct pack *pk;
{

	pk->p_ps = pk->p_pr =  pk->p_rpr = 0;
	pk->p_nxtps = 1;
}

#ifdef ULTRIX
/*
 * Vax-specific checksum generator, same checksum as portable version
 * but faster.  NOTE: ASSUMES n < 65535
 * 14 June 1983
 * Chris Torek
 * University of Maryland
 */
chksum (s, n)
register char *s;
register n;
{
	register sum, x;
	register unsigned t;

	sum = -1;
	x = 0;
	do {
		/* Rotate left, copying bit 15 to bit 0 */
		if (sum & 0x8000)
			sum <<= 1, sum++;
		else
			sum <<= 1;
		sum &= 0xffff;		/* Switch to unsigned short */
		t = sum;
		sum = (sum + (*s++ & 0377)) & 0xffff;
		x += sum ^ n;
		if ((unsigned)sum <= t)	/* (unsigned) not necessary */
			sum ^= x;	/* but doesn't hurt */
	} while (--n > 0);

	return (int) (short) sum;
}



#else

/*  old check sum routine */
chksum(s,n)
register char *s;
register n;
{
	register unsigned sum, t;
	register x;

	sum = -1;
	x = 0;

	do {
		if (sum&0x8000) {
			sum <<= 1;
			sum++;
		} else
			sum <<= 1;
		t = sum;
		sum += (unsigned)*s++ & 0377;
		x += sum^n;
		if ((sum&0xffff) <= (t&0xffff)) {
			sum ^= x;
		}
	} while (--n > 0);

	return(sum & 0xffff);
}
#endif


pkline(pk)
register struct pack *pk;
{
register i;
	for(i=0;i<NPLINES;i++) {
		if (pklines[i]==pk)
			return(i);
	}
	return(-i);
}

pkzero(s,n)
register char *s;
register n;
{
	while (n--)
		*s++ = 0;
}

pksize(n)
register n;
{
register k;

	n >>= 5;
	for(k=0; n >>= 1; k++);
	return(k);
}