text: /*
   * Copyright (c) 1999, 2002 Greg Haerr <greg@censoft.com>
   *
   * Nano-X Core Protocol Client Request Handling Routines
   */ 
  #include "uni_std.h"
  #include <stdlib.h>
  #include <assert.h>
  #include <errno.h>
  #include "serv.h"
  #include "nxproto.h"
  #include "lock.h"
  
  #define SZREQBUF	2048	/* initial request buffer size*/
  
  #if !__ECOS
  static REQBUF	reqbuf;		/* request buffer*/
  extern int 	nxSocket;
  extern char *	nxSharedMem;
  LOCK_EXTERN(nxGlobalLock);	/* global lock for threads safety*/
  #endif
  
  /* Allocate a request buffer of passed size and fill in header fields*/
  void *
  nxAllocReq(int type, long size, long extra)
  {
  	nxReq *	req;
  	long	aligned_len;
          ACCESS_PER_THREAD_DATA();
  
  	/* variable size requests must be hand-padded*/
  	if(extra)
  		assert((size & (long)(ALIGNSZ-1)) == 0);
  
  	/* calculate aligned length of request buffer*/
  	aligned_len = (size + extra + (long)(ALIGNSZ-1)) & ~(long)(ALIGNSZ-1);
  
  	/* verify we're not greater than max request size*/
  	assert(aligned_len <= MAXREQUESTSZ);
  
  	/* flush buffer if required, and allocate larger one if required*/
  	if(reqbuf.bufptr + aligned_len >= reqbuf.bufmax)
  		nxFlushReq(aligned_len,1);
  
  	/* fill in request header*/
  	req = (nxReq *)reqbuf.bufptr;
  	req->reqType = (BYTE8)type;
  	req->hilength = (BYTE8)((size + extra) >> 16);
  	req->length = (UINT16)(size + extra);
  	reqbuf.bufptr += aligned_len;
  	return req;
  }
  
  static void nxAllocReqbuffer(long newsize)
  {
          ACCESS_PER_THREAD_DATA();
  
  	if(newsize < (long)SZREQBUF)
  		newsize = SZREQBUF;
  	reqbuf.buffer = malloc(newsize);
  	if(!reqbuf.buffer) {
  		EPRINTF("nxFlushReq: Can't allocate initial request buffer\n");
  		exit(1);
  	}
  	reqbuf.bufptr = reqbuf.buffer;
  	reqbuf.bufmax = reqbuf.buffer + newsize;
  }
  
  void
  nxAssignReqbuffer(char *buffer, long size)
  {
          ACCESS_PER_THREAD_DATA();
  
  	if ( reqbuf.buffer != 0 )
  		free(reqbuf.buffer);
  	reqbuf.buffer = (unsigned char *)buffer;
  	reqbuf.bufptr = reqbuf.buffer;
  	reqbuf.bufmax = reqbuf.buffer + size;
  }
  
  /* Write a block of data on the socket to the nano-X server */
  void
  nxWriteSocket(char *buf, int todo)
  {
  	int written;
          ACCESS_PER_THREAD_DATA();
  
  	do {
  		written = write(nxSocket, buf, todo);
  		if ( written < 0 ) {
  			if ( errno == EAGAIN || errno == EINTR )
  				continue;
  			EPRINTF("nxFlushReq: write failed: errno %d\n", errno);
  			exit(1);
  		}
  		buf += written;
  		todo -= written;
  	} while ( todo > 0 );
  }
  
  /* Flush request buffer if required, possibly reallocate buffer size*/
  void
  nxFlushReq(long newsize, int reply_needed)
  {
          ACCESS_PER_THREAD_DATA();
  	LOCK(&nxGlobalLock);
  
  	/* handle one-time initialization case*/
  	if(reqbuf.buffer == NULL) {
  		nxAllocReqbuffer(newsize);
  		UNLOCK(&nxGlobalLock);
  		return;
  	}
  
  	/* flush buffer if required*/
  	if(reqbuf.bufptr > reqbuf.buffer) {
  		char *	buf = (char *)reqbuf.buffer;
  		int	todo = reqbuf.bufptr - reqbuf.buffer;
  
  #if HAVE_SHAREDMEM_SUPPORT
  		if ( nxSharedMem != 0 ) {
  			/* There is a shared memory segment used for the
  			 * request buffer.  Make up a flush command and
  			 * send it over the socket, to tell the server to
  			 * process the shared memory segment.
  			 * The 'reply_needed' argument should be non-zero
  			 * when a confirmation is needed that all commands
  			 * are flushed, so new ones can be filled into the
  			 * request buffer.  NOTE:  This is *only* needed
  			 * when explicitely flushing the request buffer, or
  			 * when flushing it to make space for new commands.
  			 * DO NOT REQUEST A REPLY when flushing the request
  			 * buffer because the last command in the buffer
  			 * will send a response:  This response would be
  			 * queued up first and had to be drained before the
  			 * response to the flush command itsel....
  			 * So the GrReadBlock used to read replys to commands
  			 * must not specify a nonzero 'reply_needed'.
  			 * Not requesting a reply in this case is
  			 * safe, since the command executed will wait for
  			 * the reply *it* is waiting for, and thus make
  			 * sure the request buffer is flushed before
  			 * continuing.
  			 *
  			 * We have to make the protocol request by hand,
  			 * as it has to be sent over the socket to wake
  			 * up the Nano-X server.
  			 */
  			char c;
  			nxShmCmdsFlushReq req;
  
  			req.reqType = GrNumShmCmdsFlush;
  			req.hilength = 0;
  			req.length = sizeof(req);
  			req.size = todo;
  			req.reply = reply_needed;
  
  			nxWriteSocket((char *)&req,sizeof(req));
  
  			if ( reply_needed )
  				while ( read(nxSocket, &c, 1) != 1 )
  					;
  
  			reqbuf.bufptr = reqbuf.buffer;
  
  			if ( reqbuf.buffer + newsize > reqbuf.bufmax ) {
  				/* Shared memory too small, critical */
  				EPRINTF("nxFlushReq: shm region too small\n");
  				exit(1);
  			}
  			UNLOCK(&nxGlobalLock);
  			return;
  		}
  #endif /* HAVE_SHAREDMEM_SUPPORT*/
  
  		/* Standard Socket transfer */
  		nxWriteSocket(buf,todo);
  		reqbuf.bufptr = reqbuf.buffer;
  	}
  
  	/* allocate larger buffer for current request, if needed*/
  	if(reqbuf.bufptr + newsize >= reqbuf.bufmax) {
  		reqbuf.buffer = REALLOC(reqbuf.buffer, reqbuf.bufmax - reqbuf.buffer, newsize);
  		if(!reqbuf.buffer) {
  		       EPRINTF("nxFlushReq: Can't reallocate request buffer\n");
  			exit(1);
  		}
  		reqbuf.bufptr = reqbuf.buffer;
  		reqbuf.bufmax = reqbuf.buffer + newsize;
  	}
  	UNLOCK(&nxGlobalLock);
  }
  
  /* calc # bytes required for passed string according to encoding*/
  int
  nxCalcStringBytes(void *str, int count, GR_TEXTFLAGS flags)
  {
  	int	nbytes;
  
  	/* calc byte length of data*/
  	if(flags & (MWTF_UC16|MWTF_XCHAR2B))
  		nbytes = count * 2;
  	else if(flags & MWTF_UC32)
  		nbytes = count * 4;
  	else
  		nbytes = count;	/* count is byte count for ascii & dbcs*/
  
  	return nbytes;
  }
  