text: /*
   * Buffering of output and input. 
   * Copyright (C) 1998 Kunihiro Ishiguro
   *
   * This file is part of GNU Zebra.
   *
   * GNU Zebra is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published
   * by the Free Software Foundation; either version 2, or (at your
   * option) any later version.
   * 
   * GNU Zebra is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with GNU Zebra; see the file COPYING.  If not, write to the
   * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   * Boston, MA 02111-1307, USA. 
   */
  
  #include <zebra.h>
  
  #include "memory.h"
  #include "buffer.h"
  
  /* Make buffer data. */
  struct buffer_data *
  buffer_data_new (size_t size)
  {
    struct buffer_data *d;
  
    d = XMALLOC (MTYPE_BUFFER_DATA, sizeof (struct buffer_data));
    memset (d, 0, sizeof (struct buffer_data));
    d->data = XMALLOC (MTYPE_BUFFER_DATA, size);
  
    return d;
  }
  
  void
  buffer_data_free (struct buffer_data *d)
  {
    if (d->data)
      XFREE (MTYPE_BUFFER_DATA, d->data);
    XFREE (MTYPE_BUFFER_DATA, d);
  }
  
  /* Make new buffer. */
  struct buffer *
  buffer_new (size_t size)
  {
    struct buffer *b;
  
    b = XMALLOC (MTYPE_BUFFER, sizeof (struct buffer));
    memset (b, 0, sizeof (struct buffer));
  
    b->size = size;
  
    return b;
  }
  
  /* Free buffer. */
  void
  buffer_free (struct buffer *b)
  {
    struct buffer_data *d;
    struct buffer_data *next;
  
    d = b->head;
    while (d)
      {
        next = d->next;
        buffer_data_free (d);
        d = next;
      }
  
    d = b->unused_head;
    while (d)
      {
        next = d->next;
        buffer_data_free (d);
        d = next;
      }
    
    XFREE (MTYPE_BUFFER, b);
  }
  
  /* Make string clone. */
  char *
  buffer_getstr (struct buffer *b)
  {
    return strdup ((char *)b->head->data);
  }
  
  /* Return 1 if buffer is empty. */
  int
  buffer_empty (struct buffer *b)
  {
    if (b->tail == NULL || b->tail->cp == b->tail->sp)
      return 1;
    else
      return 0;
  }
  
  /* Clear and free all allocated data. */
  void
  buffer_reset (struct buffer *b)
  {
    struct buffer_data *data;
    struct buffer_data *next;
    
    for (data = b->head; data; data = next)
      {
        next = data->next;
        buffer_data_free (data);
      }
    b->head = b->tail = NULL;
    b->alloc = 0;
    b->length = 0;
  }
  
  /* Add buffer_data to the end of buffer. */
  void
  buffer_add (struct buffer *b)
  {
    struct buffer_data *d;
  
    d = buffer_data_new (b->size);
  
    if (b->tail == NULL)
      {
        d->prev = NULL;
        d->next = NULL;
        b->head = d;
        b->tail = d;
      }
    else
      {
        d->prev = b->tail;
        d->next = NULL;
  
        b->tail->next = d;
        b->tail = d;
      }
  
    b->alloc++;
  }
  
  /* Write data to buffer. */
  int
  buffer_write (struct buffer *b, u_char *ptr, size_t size)
  {
    struct buffer_data *data;
  
    data = b->tail;
    b->length += size;
  
    /* We use even last one byte of data buffer. */
    while (size)    
      {
        /* If there is no data buffer add it. */
        if (data == NULL || data->cp == b->size)
  	{
  	  buffer_add (b);
  	  data = b->tail;
  	}
  
        /* Last data. */
        if (size <= (b->size - data->cp))
  	{
  	  memcpy ((data->data + data->cp), ptr, size);
  
  	  data->cp += size;
  	  size = 0;
  	}
        else
  	{
  	  memcpy ((data->data + data->cp), ptr, (b->size - data->cp));
  
  	  size -= (b->size - data->cp);
  	  ptr += (b->size - data->cp);
  
  	  data->cp = b->size;
  	}
      }
    return 1;
  }
  
  /* Insert character into the buffer. */
  int
  buffer_putc (struct buffer *b, u_char c)
  {
    buffer_write (b, &c, 1);
    return 1;
  }
  
  /* Insert word (2 octets) into ther buffer. */
  int
  buffer_putw (struct buffer *b, u_short c)
  {
    buffer_write (b, (char *)&c, 2);
    return 1;
  }
  
  /* Put string to the buffer. */
  int
  buffer_putstr (struct buffer *b, u_char *c)
  {
    size_t size;
  
    size = strlen ((char *)c);
    buffer_write (b, c, size);
    return 1;
  }
  
  /* Flush specified size to the fd. */
  void
  buffer_flush (struct buffer *b, int fd, size_t size)
  {
    int iov_index;
    struct iovec *iovec;
    struct buffer_data *data;
    struct buffer_data *out;
    struct buffer_data *next;
  
    iovec = malloc (sizeof (struct iovec) * b->alloc);
    iov_index = 0;
  
    for (data = b->head; data; data = data->next)
      {
        iovec[iov_index].iov_base = (char *)(data->data + data->sp);
  
        if (size <= (data->cp - data->sp))
  	{
  	  iovec[iov_index++].iov_len = size;
  	  data->sp += size;
  	  if (data->sp == data->cp)
  	    data = data->next;
  	  break;
  	}
        else
  	{
  	  iovec[iov_index++].iov_len = data->cp - data->sp;
  	  size -= data->cp - data->sp;
  	  data->sp = data->cp;
  	}
      }
  
    /* Write buffer to the fd. */
    writev (fd, iovec, iov_index);
  
    /* Free printed buffer data. */
    for (out = b->head; out && out != data; out = next)
      {
        next = out->next;
        if (next)
  	next->prev = NULL;
        else
  	b->tail = next;
        b->head = next;
  
        buffer_data_free (out);
        b->alloc--;
      }
  
    free (iovec);
  }
  
  /* Flush all buffer to the fd. */
  int
  buffer_flush_all (struct buffer *b, int fd)
  {
    int ret;
    struct buffer_data *d;
    int iov_index;
    struct iovec *iovec;
  
    if (buffer_empty (b))
      return 0;
  
    iovec = malloc (sizeof (struct iovec) * b->alloc);
    iov_index = 0;
  
    for (d = b->head; d; d = d->next)
      {
        iovec[iov_index].iov_base = (char *)(d->data + d->sp);
        iovec[iov_index].iov_len = d->cp - d->sp;
        iov_index++;
      }
    ret = writev (fd, iovec, iov_index);
  
    free (iovec);
  
    buffer_reset (b);
  
    return ret;
  }
  
  /* Flush all buffer to the fd. */
  int
  buffer_flush_vty_all (struct buffer *b, int fd, int erase_flag,
  		      int no_more_flag)
  {
    int nbytes;
    int iov_index;
    struct iovec *iov;
    struct iovec small_iov[3];
    char more[] = " --More-- ";
    char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
  		   ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
  		   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
    struct buffer_data *data;
    struct buffer_data *out;
    struct buffer_data *next;
  
    /* For erase and more data add two to b's buffer_data count.*/
    if (b->alloc == 1)
      iov = small_iov;
    else
      iov = XMALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2));
  
    data = b->head;
    iov_index = 0;
  
    /* Previously print out is performed. */
    if (erase_flag)
      {
        iov[iov_index].iov_base = erase;
        iov[iov_index].iov_len = sizeof erase;
        iov_index++;
      }
  
    /* Output data. */
    for (data = b->head; data; data = data->next)
      {
        iov[iov_index].iov_base = (char *)(data->data + data->sp);
        iov[iov_index].iov_len = data->cp - data->sp;
        iov_index++;
      }
  
    /* In case of `more' display need. */
    if (! buffer_empty (b) && !no_more_flag)
      {
        iov[iov_index].iov_base = more;
        iov[iov_index].iov_len = sizeof more;
        iov_index++;
      }
  
    /* We use write or writev*/
    nbytes = writev (fd, iov, iov_index);
  
    /* Error treatment. */
    if (nbytes < 0)
      {
        if (errno == EINTR)
  	;
        if (errno == EWOULDBLOCK)
  	;
      }
  
    /* Free printed buffer data. */
    for (out = b->head; out && out != data; out = next)
      {
        next = out->next;
        if (next)
  	next->prev = NULL;
        else
  	b->tail = next;
        b->head = next;
  
        buffer_data_free (out);
        b->alloc--;
      }
  
    if (iov != small_iov)
      XFREE (MTYPE_TMP, iov);
  
    return nbytes;
  }
  
  /* Flush buffer to the file descriptor.  Mainly used from vty
     interface. */
  int
  buffer_flush_vty (struct buffer *b, int fd, int size, 
  		  int erase_flag, int no_more_flag)
  {
    int nbytes;
    int iov_index;
    struct iovec *iov;
    struct iovec small_iov[3];
    char more[] = " --More-- ";
    char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
  		   ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
  		   0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
    struct buffer_data *data;
    struct buffer_data *out;
    struct buffer_data *next;
  
  #ifdef  IOV_MAX
    int iov_size;
    int total_size;
    struct iovec *c_iov;
    int c_nbytes;
  #endif /* IOV_MAX */
  
    /* For erase and more data add two to b's buffer_data count.*/
    if (b->alloc == 1)
      iov = small_iov;
    else
      iov = XMALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2));
  
    data = b->head;
    iov_index = 0;
  
    /* Previously print out is performed. */
    if (erase_flag)
      {
        iov[iov_index].iov_base = erase;
        iov[iov_index].iov_len = sizeof erase;
        iov_index++;
      }
  
    /* Output data. */
    for (data = b->head; data; data = data->next)
      {
        iov[iov_index].iov_base = (char *)(data->data + data->sp);
  
        if (size <= (data->cp - data->sp))
  	{
  	  iov[iov_index++].iov_len = size;
  	  data->sp += size;
  	  if (data->sp == data->cp)
  	    data = data->next;
  	  break;
  	}
        else
  	{
  	  iov[iov_index++].iov_len = data->cp - data->sp;
  	  size -= (data->cp - data->sp);
  	  data->sp = data->cp;
  	}
      }
  
    /* In case of `more' display need. */
    if (!buffer_empty (b) && !no_more_flag)
      {
        iov[iov_index].iov_base = more;
        iov[iov_index].iov_len = sizeof more;
        iov_index++;
      }
  
    /* We use write or writev*/
  
  #ifdef IOV_MAX
    /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
       example: Solaris2.6 are defined IOV_MAX size at 16.     */
    c_iov = iov;
    total_size = iov_index;
    nbytes = 0;
  
    while( total_size > 0 )
      {
         /* initialize write vector size at once */
         iov_size = ( total_size > IOV_MAX ) ? IOV_MAX : total_size;
  
         c_nbytes = writev (fd, c_iov, iov_size );
  
         if( c_nbytes < 0 )
           {
             if(errno == EINTR)
               ;
               ;
             if(errno == EWOULDBLOCK)
               ;
               ;
             nbytes = c_nbytes;
             break;
  
           }
  
          nbytes += c_nbytes;
  
         /* move pointer io-vector */
         c_iov += iov_size;
         total_size -= iov_size;
      }
  #else  /* IOV_MAX */
     nbytes = writev (fd, iov, iov_index);
  
    /* Error treatment. */
    if (nbytes < 0)
      {
        if (errno == EINTR)
  	;
        if (errno == EWOULDBLOCK)
  	;
      }
  #endif /* IOV_MAX */
  
    /* Free printed buffer data. */
    for (out = b->head; out && out != data; out = next)
      {
        next = out->next;
        if (next)
  	next->prev = NULL;
        else
  	b->tail = next;
        b->head = next;
  
        buffer_data_free (out);
        b->alloc--;
      }
  
    if (iov != small_iov)
      XFREE (MTYPE_TMP, iov);
  
    return nbytes;
  }
  
  /* Calculate size of outputs then flush buffer to the file
     descriptor. */
  int
  buffer_flush_window (struct buffer *b, int fd, int width, int height, 
  		     int erase, int no_more)
  {
    unsigned long cp;
    unsigned long size;
    int lp;
    int lineno;
    struct buffer_data *data;
  
    if (height >= 2)
      height--;
  
    /* We have to calculate how many bytes should be written. */
    lp = 0;
    lineno = 0;
    size = 0;
    
    for (data = b->head; data; data = data->next)
      {
        cp = data->sp;
  
        while (cp < data->cp)
  	{
  	  if (data->data[cp] == '\n' || lp == width)
  	    {
  	      lineno++;
  	      if (lineno == height)
  		{
  		  cp++;
  		  size++;
  		  goto flush;
  		}
  	      lp = 0;
  	    }
  	  cp++;
  	  lp++;
  	  size++;
  	}
      }
  
    /* Write data to the file descriptor. */
   flush:
  
    return buffer_flush_vty (b, fd, size, erase, no_more);
  }
  