/* source: xiowrite.c */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* Published under the GNU General Public License V.2, see file COPYING */

/* this is the source of the extended write function */


#include "xiosysincludes.h"
#include "xioopen.h"

#include "xio-posixmq.h"
#include "xio-readline.h"
#include "xio-openssl.h"


/* ...
   note that the write() call can block even if the select()/poll() call
   reported the FD writeable: in case the FD is not nonblocking and a lock
   defers the operation.
   on return value < 0: errno reflects the value from write() */
ssize_t xiowrite(xiofile_t *file, const void *buff, size_t bytes) {
   ssize_t writt;
   struct single *pipe;
   int _errno;

   if (file->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) {
      Error1("xiowrite(): invalid xiofile descriptor %p", file);
      errno = EINVAL;
      return -1;
   }

   if (file->tag == XIO_TAG_DUAL) {
      pipe = file->dual.stream[1];
      if (pipe->tag == XIO_TAG_INVALID || file->tag & XIO_TAG_CLOSED) {
	 Error1("xiowrite(): invalid xiofile sub descriptor %p[1]", file);
	 errno = EINVAL;
	 return -1;
      }
   } else {
      pipe = &file->stream;
   }

#if WITH_READLINE
   /* try to extract a prompt from the write data */
   if ((pipe->dtype & XIODATA_READMASK) == XIOREAD_READLINE) {
      xioscan_readline(pipe, buff, bytes);
   }
#endif /* WITH_READLINE */

   switch (pipe->dtype & XIODATA_WRITEMASK) {

   case XIOWRITE_STALL:
      /* Should not be reached with STALL address */
      Debug1("xiowrite(): dropping "F_Zu" bytes", bytes);
      writt = bytes;
      break;

   case XIOWRITE_STREAM:
      writt = writefull(pipe->fd, buff, bytes, NULL);
      if (writt < 0) {
	 _errno = errno;
	 switch (_errno) {
	 case EPIPE:
	 case ECONNRESET:
	    if (pipe->cool_write) {
	       Notice4("write(%d, %p, "F_Zu"): %s",
		       pipe->fd, buff, bytes, strerror(_errno));
	       break;
	    }
	    /*PASSTRHOUGH*/
	 default:
	    Error4("write(%d, %p, "F_Zu"): %s",
		   pipe->fd, buff, bytes, strerror(_errno));
	 }
	 errno = _errno;
	 return -1;
      }
      break;

#if _WITH_SOCKET
   case XIOWRITE_SENDTO:
      /*union {
	 char space[sizeof(struct sockaddr_un)];
	 struct sockaddr sa;
	 } from;*/
      /*socklen_t fromlen;*/

      do {
	 writt = Sendto(pipe->fd, buff, bytes, 0,
			&pipe->peersa.soa, pipe->salen);
      } while (writt < 0 && errno == EINTR);
      if (writt < 0) {
	 char infobuff[256];
	 _errno = errno;
	 Error6("sendto(%d, %p, "F_Zu", 0, %s, "F_socklen"): %s",
		pipe->fd, buff, bytes,
		sockaddr_info(&pipe->peersa.soa, pipe->salen,
			      infobuff, sizeof(infobuff)),
		pipe->salen, strerror(_errno));
	 errno = _errno;
	 return -1;
      }
      if ((size_t)writt < bytes) {
	 char infobuff[256];
	 Warn7("sendto(%d, %p, "F_Zu", 0, %s, "F_socklen") only sent "F_Zu" of "F_Zu" bytes",
	       pipe->fd, buff, bytes,
	       sockaddr_info(&pipe->peersa.soa, pipe->salen,
			     infobuff, sizeof(infobuff)),
	       pipe->salen, writt, bytes);
      }
      {
	 char infobuff[256];
	 union sockaddr_union us;
	 socklen_t uslen = sizeof(us);
	 Getsockname(pipe->fd, &us.soa, &uslen);
	 Notice1("local address: %s",
		 sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff)));
      }
      break;
#endif /* _WITH_SOCKET */

   case XIOWRITE_PIPE:
      if (pipe->para.bipipe.socktype == SOCK_STREAM) {
	 writt = Write(pipe->para.bipipe.fdout, buff, bytes);
#if _WITH_SOCKET
      } else {
	 writt = Send(pipe->para.bipipe.fdout, buff, bytes, 0);
#endif /* _WITH_SOCKET */
      }
      _errno = errno;
      if (writt < 0) {
	 Error4("write(%d, %p, "F_Zu"): %s",
		pipe->para.bipipe.fdout, buff, bytes, strerror(_errno));
	 errno = _errno;
	 return -1;
      }
      break;

   case XIOWRITE_2PIPE:
      writt = Write(pipe->para.exec.fdout, buff, bytes);
      _errno = errno;
      if (writt < 0) {
	 Error4("write(%d, %p, "F_Zu"): %s",
		pipe->para.exec.fdout, buff, bytes, strerror(_errno));
	 errno = _errno;
	 return -1;
      }
      break;

#if WITH_POSIXMQ
   case XIOWRITE_POSIXMQ:
      if ((writt = xiowrite_posixmq(pipe, buff, bytes)) < 0) {
	 return -1;
      }
      break;
#endif /* WITH_POSIXMQ */

#if WITH_OPENSSL
   case XIOWRITE_OPENSSL:
      /* this function prints its own error messages */
      return xiowrite_openssl(pipe, buff, bytes);
#endif /* WITH_OPENSSL */

   default:
      Error1("xiowrite(): bad data type specification %d", pipe->dtype);
      errno = EINVAL;
      return -1;
   }
   return writt;
}
