#ident "$Id: msg.c,v 1.3 1997/10/22 21:52:23 gary Exp $"
/*****************************************************************************
 * Copyright (c) 1988-95 Digi International Inc., All Rights Reserved
 * 
 * This  software  contains  proprietary  and  confidential   information   of
 * Digi International  Inc.   By  accepting  transfer  of this copy, Recipient
 * agrees to retain this software in  confidence,  to  prevent  disclosure  to
 * others,  and  to make no  use of this software other than that for which it
 * was delivered.  This is an unpublished copyright work  Digi   International
 * Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is
 * strictly prohibited.
 * 
 * Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc.
 * Confidential  when   combined   with   the   aggregated  modules  for  this
 * product) OBJECT CODE ONLY SOURCE MATERIALS
 * 
 * (C) COPYRIGHT Digi International Inc. 1995
 * 
 * All Rights Reserved
 * 
 * US   Government   Users   Restricted   Rights   -   Use,   duplication   or
 * disclosure  restricted by GSA ADP Schedule Contract with Digi International
 * Inc.
 * 
 *****************************************************************************/

#define	WATCHPOINT
/*
 * Msg.c - Message Management Functions.
 *
 * @(#)msg.c	1.13 (Spider Software) 2/3/93
 *
 * Copyright (C) 1992 Spider Systems Limited
 *
 * This Source Code is furnished under Licence, and may not be copied or
 * distributed without express written agreement.
 *
 * All rights reserved.
 * 
 * History:
 * 
 * 	For SpiderStreams2.1	MJSC		15 Nov 1992
 */


#include "types.h"
#include "stream.h"

#ifdef __STDC__
extern aerror(void *, int, int) ;  /* prototype */
#endif

#ifdef SOH
#include "sohalloc.h"
#endif

#ifdef allocb
#undef allocb
#define allocb(sz, pri) Xallocb(sz, pri, 0-__LINE__, "msg.c")
#else
extern mblk_t *allocb();
#endif
extern mblk_t *allocm();

/************************/
/*	adjmsg		*/
/************************/
int adjmsg(mp, len)
mblk_t *mp;
int	len;
{
	int type;
	register mblk_t *bp, *xp;
WATCHPOINT

	/* No action if length zero */
	if (len == 0)
		return (1);

	/* Check sign of length */
	if (len > 0)
	{   /* Length positive: adjust start of message */
		int n;

		type = mp->b_datap->db_type;

		/*
		 * We go down message until the block in which the new
		 * message start occurs.
		 * If the end of the part being removed is exactly at
		 * a block end, we move on to the start of the next
		 * block (unless there is none, in which case we stop
		 * at the last block - which will eventually be left
		 * empty).
		 */

		for (bp = mp; len > 0 && (len > (n = (int)(bp->b_wptr - bp->b_rptr)) || 
			len == n && bp->b_cont); )
		{   /* Go down message until right block found */
			len -= n;
			xp = bp;

			if ((bp = bp->b_cont) == (mblk_t *)0 ||
				len > 0 && bp->b_datap->db_type != type)
			{   /* Run off end or change of type */
				return (0);
			}
		}

		bp->b_rptr += len;

		if (bp != mp)
		{	/* We have reached a block down the chain. */

			/* swap roles of *mp and *bp & free unwanted blocks */
			dblk_t FAR *datap;
			mblk_t *cont;

			/* Mark end of part to discard */
			xp->b_cont = (mblk_t *)0;

			/* Save fields of *mp */
			datap = mp->b_datap;
			cont = mp->b_cont;

			/* Copy fields of *bp to *mp */
			mp->b_cont = bp->b_cont;
			mp->b_wptr = bp->b_wptr;
			mp->b_rptr = bp->b_rptr;
			mp->b_datap = bp->b_datap;

			/* Put saved fields of *mp back into *bp */
			bp->b_cont = cont;
			bp->b_datap = datap;

			/* Free unwanted blocks */
			freemsg(bp);
		}
		return (1);
	}

	/* Here if len is < 0.  We must trim bytes off of the end. */

	/* Set len = amount to keep by adding total length */
	bp = mp;
	do
		len += (int)(bp->b_wptr - bp->b_rptr);
	while (bp = bp->b_cont);

	/* Check that message is long enough */
	if (len < 0)
	{   /* Too short */
		return (0);
	}

	/* Find start of data to discard */
	bp = mp;
	do
	{   /* Go down message until right block found */
		xp = bp;
		len -= (int)(bp->b_wptr - bp->b_rptr);
	}
	while ((bp = bp->b_cont) &&
		(len > 0 || len == 0 && bp->b_wptr == bp->b_rptr));

	/* Throw away unused blocks, if any, and check that their types match */
	if (mp = bp)
	{
		type = (len ? xp : bp)->b_datap->db_type;
		while (bp = bp->b_cont)
		{
			if (bp->b_datap->db_type != type)
				/* Report failure */
				return (0);
		}
		freemsg(mp);
		xp->b_cont = 0;
	}

	/* Adjust last block */
	xp->b_wptr += len;

	/* Report success */
	return (1);
}


/************************/
/*	copyb		*/
/************************/
mblk_t *copyb(mp)
mblk_t *mp;
{
	mblk_t *cp;
	dblk_t FAR *dp = mp->b_datap;
	int sz;
WATCHPOINT

	/* Get block for copy */
	cp = allocb((int)(dp->db_lim - dp->db_base), BPRI_MED);
	if (cp)
	{   /* Copy to same offset in new block */
		bcopy((char *)mp->b_rptr,
			(char *)(cp->b_rptr += (mp->b_rptr - dp->db_base)),
			sz = (int)(mp->b_wptr - mp->b_rptr));

		cp->b_wptr = cp->b_rptr + sz;

		/* Copy type across */
		cp->b_datap->db_type = mp->b_datap->db_type ;
	}

WATCHPOINT
	/* Return pointer (or NULL) */
	return (cp);
}


/************************/
/*	copymsg		*/
/************************/
mblk_t *copymsg(mp)
mblk_t *mp;
{
	mblk_t *cp, *xp, *bp;
WATCHPOINT

	/* Copy first block */
	if (cp = xp = copyb(mp))
	{
		/* Copy and append following blocks */
		while (mp = mp->b_cont)
		{
			if (bp = copyb(mp))
			{   /* Copy OK, so chain on */
				xp->b_cont = bp;
				xp = bp;
				continue;
			}

			/* Failed to get following block */
WATCHPOINT
			freemsg(cp);
			return ((mblk_t *)0);
		}
WATCHPOINT
		/* Copy successful */
		return (cp);
	}
WATCHPOINT

	/* Failed to copy first block */
	return ((mblk_t *)0);
}


/************************/
/*	dupb		*/
/************************/
mblk_t *dupb(mp)
mblk_t *mp;
{   /* Make duplicate of message block descriptor '*mp' */

	register mblk_t *cp;
WATCHPOINT

	/* Fail if no message blocks left */
	if ((cp = allocm(BPRI_MED)) == (mblk_t *)0)
	{
		return ((mblk_t *)0);
	}

	/* Fill in new block and increment data reference count */
	cp->b_next = cp->b_prev = cp->b_cont = (mblk_t *)0;
	cp->b_rptr = mp->b_rptr;
	cp->b_wptr = mp->b_wptr;
	(cp->b_datap = mp->b_datap)->db_ref++;

#ifdef MDEBUG
	printf("M%04x + <DUPM> %04x [%3d, %3d]\n", (uint)cp,
		(uint)cp->b_datap, cp->b_datap->db_class, cp->b_datap->db_ref);
#endif
	/* Successful result */
WATCHPOINT
	return (cp);
}


/************************/
/*	dupmsg		*/
/************************/
mblk_t *dupmsg(mp)
register mblk_t *mp;
{   /* Make duplicate of whole message 'mp' */
	mblk_t *cp, *xp, *bp;
#ifdef DEBUG
	int loops = 0;
#endif

WATCHPOINT
	/* Dup first block */
	xp = cp = dupb(mp);
	if (cp)
	{
		/* Dup and append following blocks */
		while(mp = mp->b_cont)
		{
#ifdef DEBUG
			loops++;
#endif
			if (bp = dupb(mp))
			{   /* Dup OK, so chain on */
				xp->b_cont = bp;
				xp = bp;
				continue;
			}

			/* Failed to get following block */
			freemsg(cp);
#ifdef DEBUG
printf("dupmsg failed after %d loops\n", loops);
#endif 
			return (mblk_t *)0;
		}

		/* Dup successful */
WATCHPOINT
		return (cp);
	}

	/* Failed to dup first block */
	return ((mblk_t *)0);
}


/************************/
/*	linkb		*/
/************************/
void linkb(mp1, mp2)
mblk_t *mp1, *mp2;
{
	mblk_t *bp;
WATCHPOINT

	/* Find end of mp1 */
	while (bp = mp1, mp1 = mp1->b_cont);

	/* Append mp2 */
	bp->b_cont = mp2;
}


/************************/
/*	msgdsize	*/
/************************/
int msgdsize(mp)
mblk_t *mp;
{
	int sz = 0;
WATCHPOINT
	do
		if (mp->b_datap->db_type == M_DATA)
			sz += (int)(mp->b_wptr - mp->b_rptr);
	while (mp = mp->b_cont);
	return (sz);
}

#ifdef BAR
#define aligned(ptr) (1)
#else
#define aligned(ptr)	(!((int3264)(ptr) & (sizeof(int3264) - 1)))
#endif

/************************/
/*	pullupmsg	*/
/************************/
int pullupmsg(mp, len)
mblk_t *mp;
int	len;
{
	mblk_t *cp, *bp, *cont;
	int sz;
	unchar type = mp->b_datap->db_type;
WATCHPOINT

	/* If len == -1, find actual length of initial type */
	if (len == -1)
	{
		if (mp->b_cont == 0)
			return (1);	/* Nothing to do */

		for (bp = mp, len = 0;
			bp && bp->b_datap->db_type == type;
			bp = bp->b_cont)
		len += (int)(bp->b_wptr - bp->b_rptr);
	}

	if ((mp->b_wptr - mp->b_rptr) >= len)
	{
		/* the data to pull-up is wholly contained in the first block */

		/* if it is already aligned, we have nothing to do */
		if (aligned(mp->b_rptr)) return (1);

		if (mp->b_datap->db_ref == 1)
		{	/* Just one ref, safe to copy in situ.
			 * Since we know the buffer starts on an alignment
			 * boundary, we can just copy backwards.
			 * Don't use the system copy routine for this -
			 * as it is overlapping
			 */

			unsigned char FAR *sp = mp->b_rptr;
			unsigned char FAR *ep = mp->b_wptr;
			unsigned char FAR *dp;

			mp->b_rptr = dp = (unsigned char FAR *)(straln(sp));

			while (sp < ep)
				*dp++ = *sp++;

			mp->b_wptr = dp;
			return (1);
		}
	} 
	else
	{
		/*
		 * find length of data, so we can fail the call if
		 * there is not enough, BEFORE we start munging it
		 */
		int actlen = 0;
		for (bp = mp; bp && bp->b_datap->db_type == type;
			bp = bp->b_cont)
		actlen += (int)(bp->b_wptr - bp->b_rptr);
		if (actlen < len)
			return (0);
	}

	/* Now we can get data block */
	if (cp = allocb(len, BPRI_MED))
	{
		/* as 'mp' header cannot be freed, we switch 'mp' and 'cp' */
		mblk_t tmp_b;
		tmp_b = *cp;
		*cp = *mp;
		*mp = tmp_b;

		/*
		 * "mp" now references to the newly allocated block,
		 * while "cp" references the block starting the message
		 */

		/* Set up first block */
		mp->b_datap->db_type = type;

		while (len)
		{
			/* Amount of block to copy */
			if ((sz = (int)(cp->b_wptr - cp->b_rptr)) > len) sz = len;

			/* Do the copy */
			bcopy((char *)cp->b_rptr, (char *)(mp->b_wptr), sz);

			mp->b_wptr += sz;
			cp->b_rptr += sz;
			len -= sz;

			if (cp->b_rptr == cp->b_wptr)	/* block empty */
			{
				cont = cp->b_cont;
				cp->b_cont = (mblk_t *) 0; /* let's be tidy */
				freeb(cp);
				cp = cont;
			}
		}

		/* tag remaining blocks after the new one */
		mp->b_cont = cp;
WATCHPOINT
		return (1);
	}
	else
	{
WATCHPOINT
	    return (0);		/* could not allocate the block */
	}
}

/************************/
/*	rmvb		*/
/************************/
mblk_t *rmvb(mp, bp)
mblk_t *mp, *bp;
{
	mblk_t *xp;
WATCHPOINT

	if (mp == bp)
	{   /* Top block */
		bp = mp->b_cont;
		mp->b_cont = (mblk_t *)0;
		return (bp);
	}

	/* Look down chain */
	xp = mp;
	while (xp && xp->b_cont != bp) xp = xp->b_cont;
	if (xp)
	{
		xp->b_cont = bp->b_cont;
		bp->b_cont = (mblk_t *)0;
		return (mp);
	}
	return ((mblk_t *)-1);
}

/************************/
/*	unlinkb		*/
/************************/
mblk_t *unlinkb(mp)
mblk_t *mp;
{
	register mblk_t *bp = mp->b_cont;
WATCHPOINT
	mp->b_cont = (mblk_t *)0;
	return (bp);
}
