From 718e3744195351130f4ce7dbe0613f4b3e23df93 Mon Sep 17 00:00:00 2001 From: paul Date: Fri, 13 Dec 2002 20:15:29 +0000 Subject: Initial revision --- lib/buffer.c | 568 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 568 insertions(+) create mode 100644 lib/buffer.c (limited to 'lib/buffer.c') diff --git a/lib/buffer.c b/lib/buffer.c new file mode 100644 index 00000000..de51ee3e --- /dev/null +++ b/lib/buffer.c @@ -0,0 +1,568 @@ +/* + * 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 + +#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 = XCALLOC (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 = XCALLOC (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 , 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); +} -- cgit v1.2.1